use std::fs::File;
use std::mem::ManuallyDrop;
use crate::backend::LockBackend;
use crate::error::Result;
use crate::sys;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum LockKind {
Shared,
Exclusive,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum LockMode {
Blocking,
NonBlocking,
}
#[derive(Debug)]
pub struct FileLock {
pub(crate) file: ManuallyDrop<File>,
pub(crate) kind: LockKind,
pub(crate) backend: LockBackend,
pub(crate) armed: bool,
}
impl FileLock {
pub(crate) fn new(file: File, kind: LockKind, backend: LockBackend) -> Self {
Self {
file: ManuallyDrop::new(file),
kind,
backend,
armed: true,
}
}
pub fn lock(file: File, kind: LockKind, mode: LockMode) -> Result<Self> {
let backend = LockBackend::Flock;
sys::lock(&file, kind, mode, backend)?;
Ok(Self::new(file, kind, backend))
}
#[inline]
pub fn kind(&self) -> LockKind {
self.kind
}
#[inline]
pub fn backend(&self) -> LockBackend {
self.backend
}
#[inline]
pub fn file(&self) -> &File {
&self.file
}
#[inline]
pub fn file_mut(&mut self) -> &mut File {
&mut self.file
}
pub fn upgrade(&mut self, mode: LockMode) -> Result<()> {
if self.kind == LockKind::Exclusive {
return Ok(());
}
sys::upgrade(self.file(), mode, self.backend)?;
self.kind = LockKind::Exclusive;
Ok(())
}
pub fn downgrade(&mut self) -> Result<()> {
if self.kind == LockKind::Shared {
return Ok(());
}
sys::downgrade(self.file(), self.backend)?;
self.kind = LockKind::Shared;
Ok(())
}
pub fn unlock(mut self) -> std::result::Result<File, (File, crate::Error)> {
let unlock_res = sys::unlock(&self.file, self.backend);
self.armed = false;
let file = unsafe { ManuallyDrop::take(&mut self.file) };
match unlock_res {
Ok(()) => Ok(file),
Err(e) => Err((file, e)),
}
}
pub fn into_file(mut self) -> File {
self.armed = false;
unsafe { ManuallyDrop::take(&mut self.file) }
}
}
impl Drop for FileLock {
fn drop(&mut self) {
if self.armed {
let _ = sys::unlock(&self.file, self.backend);
unsafe { ManuallyDrop::drop(&mut self.file) };
}
}
}