use core::future::{Future, poll_fn};
use core::pin::Pin;
use core::sync::atomic::{AtomicU8, Ordering};
use core::task::{Context, Poll};
use std::fs::{File, TryLockError};
use std::io;
use std::sync::Arc;
use futures_io::AsyncWrite;
use crate::io::Handle;
use crate::io::source::requests::Lease;
use crate::rt::task::Join;
const WAITING: u8 = 0;
const HELD: u8 = 1;
const GONE: u8 = 2;
struct LockCall {
state: Arc<AtomicU8>,
lease: Lease,
job: Join<io::Result<()>>,
settled: bool,
}
impl Future for LockCall {
type Output = io::Result<()>;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let ended = core::task::ready!(Pin::new(&mut self.job).poll(cx));
self.settled = true;
Poll::Ready(ended.unwrap_or_else(|lost| Err(lost.into())))
}
}
impl Drop for LockCall {
fn drop(&mut self) {
if !self.settled && self.state.swap(GONE, Ordering::AcqRel) == HELD {
let _ = self.lease.file().unlock();
}
}
}
impl Handle<File> {
pub async fn lock_file(&self, shared: bool) -> io::Result<()> {
let lease = self.source().lease()?;
let state = Arc::new(AtomicU8::new(WAITING));
let (job_lease, job_state) = (lease.clone(), Arc::clone(&state));
let job = crate::runtime::spawn_blocking(move || {
let file = job_lease.file();
let locked = if shared {
file.lock_shared()
} else {
file.lock()
};
if locked.is_ok()
&& job_state
.compare_exchange(WAITING, HELD, Ordering::AcqRel, Ordering::Acquire)
.is_err()
{
let _ = file.unlock();
}
locked
})
.join();
LockCall {
state,
lease,
job,
settled: false,
}
.await
}
pub async fn try_lock_file(&self, shared: bool) -> Result<(), TryLockError> {
let lease = self.source().lease().map_err(TryLockError::Error)?;
let file = lease.file();
if shared {
file.try_lock_shared()
} else {
file.try_lock()
}
}
pub async fn unlock_file(&self) -> io::Result<()> {
let mut this = self;
let flushed = poll_fn(|cx| Pin::new(&mut this).poll_flush(cx)).await;
let unlocked = self
.source()
.lease()
.and_then(|lease| lease.file().unlock());
flushed.and(unlocked)
}
}