use core::future::{Future, poll_fn};
use core::marker::PhantomData;
use core::mem::ManuallyDrop;
use core::pin::Pin;
use core::task::{Context, Poll, Waker};
use std::io;
use std::sync::Arc;
use super::work::{LentRead, tally_read};
use super::{FileHooks, FileIo, deferred};
use crate::buf::owned::{IoBuf, IoBufMut, OwnedBuf};
use crate::io::side::After;
impl FileIo {
pub(crate) fn read_owned<B>(self: &Arc<Self>, buf: B) -> ReadOwned<'_, B>
where
B: IoBufMut,
{
ReadOwned {
io: self,
held: Some(OwnedBuf::filled(buf)),
job: ManuallyDrop::new(None),
buf: PhantomData,
}
}
fn poll_read_owned(
self: &Arc<Self>,
cx: &mut Context<'_>,
held: &mut Option<OwnedBuf>,
job: &mut Option<LentRead>,
) -> Poll<io::Result<usize>> {
if held.as_ref().is_some_and(|b| b.len() == b.capacity()) {
return Poll::Ready(Ok(0));
}
let waker = cx.waker();
let mut after = After::default();
let hooks = FileHooks { io: self };
let out = self.state.poll(&mut after, waker, &hooks, |st, after| {
loop {
if let Some(lent) = job.as_mut() {
let pooled = lent.task.pooled();
let (result, mut buf) = match lent.task.poll(waker) {
Poll::Pending => return Poll::Pending,
Poll::Ready(out) => out,
};
tally_read(pooled, &result, |n| *n);
let (at, epoch, generation, from) =
(lent.at, lent.epoch, lent.generation, lent.from);
*job = None;
if !st.counts(self, at, epoch, generation) {
buf.truncate(from);
*held = Some(buf);
continue;
}
*held = Some(buf);
if let Ok(n) = result {
st.cursor += n as u64;
if let Some(ahead) = st.ahead.take() {
after.drop_buf(ahead);
}
#[cfg(all(target_os = "linux", target_pointer_width = "64"))]
if st.cache == super::cached::Cache::Cold {
st.cache = super::cached::Cache::Try;
}
}
return Poll::Ready(result);
}
if !st.writes_idle() {
self.waiters.register(waker);
let file = Waker::from(Arc::clone(self));
if st.poll_writes(self, &file, after).is_pending() {
return Poll::Pending;
}
}
let Some(buf) = held.as_mut() else {
return Poll::Ready(Err(io::Error::other("the read lost its buffer")));
};
st.drop_stale_ahead(self, after);
if let Some(ahead) = st.ahead.as_mut().filter(|a| !a.is_empty()) {
let n = crate::buf::append_spare(ahead.filled(), buf);
ahead.consume(n);
st.consumed(n, after);
return Poll::Ready(Ok(n));
}
let (at, from) = (st.cursor, buf.len());
let generation = self.generation();
#[cfg(all(target_os = "linux", target_pointer_width = "64"))]
if self.reads_cached()
&& let super::cached::Now::Read(n) =
super::cached::read(self.fd.raw(), buf, at, &mut st.cache)
{
let result = Ok(n);
tally_read(true, &result, |n| *n);
if !st.counts(self, at, st.epoch, generation) {
buf.truncate(from);
continue;
}
st.cursor += n as u64;
if let Some(ahead) = st.ahead.take() {
after.drop_buf(ahead);
}
return Poll::Ready(result);
}
let task = self.submit_read_owned(held, at, waker);
let pooled = task.pooled();
*job = Some(LentRead {
task,
at,
epoch: st.epoch,
generation,
from,
});
if !pooled {
return Poll::Pending;
}
}
});
after.run();
out
}
pub(crate) async fn write_owned<B>(self: &Arc<Self>, buf: B) -> (io::Result<usize>, B)
where
B: IoBuf,
{
let mut op = WriteOwned {
io: self,
held: Some(OwnedBuf::written(buf)),
out: false,
};
let result = poll_fn(|cx| op.poll(cx)).await;
(result, op.held.take().unwrap_or_default().into_written())
}
}
pub(crate) struct ReadOwned<'a, B> {
io: &'a Arc<FileIo>,
held: Option<OwnedBuf>,
job: ManuallyDrop<Option<LentRead>>,
buf: PhantomData<fn() -> B>,
}
impl<B> Future for ReadOwned<'_, B>
where
B: IoBufMut,
{
type Output = (io::Result<usize>, B);
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.get_mut();
let result = core::task::ready!(this.io.poll_read_owned(cx, &mut this.held, &mut this.job));
Poll::Ready((result, this.held.take().unwrap_or_default().into_filled()))
}
}
impl<B> Drop for ReadOwned<'_, B> {
#[inline]
fn drop(&mut self) {
if self.job.is_some() {
drop_cold(unsafe { ManuallyDrop::take(&mut self.job) });
}
}
}
#[cold]
#[inline(never)]
fn drop_cold<T>(value: T) {
drop(value);
}
struct WriteOwned<'a> {
io: &'a Arc<FileIo>,
held: Option<OwnedBuf>,
out: bool,
}
impl WriteOwned<'_> {
fn poll(&mut self, cx: &mut Context<'_>) -> Poll<io::Result<usize>> {
let held = &mut self.held;
let poll = self.io.with_state(cx, |st, io, waker, after| {
st.write_owned(io, held, waker, after)
});
self.out = self.held.is_none();
poll
}
}
impl Drop for WriteOwned<'_> {
fn drop(&mut self) {
if self.out {
let mut after = After::default();
self.io
.state
.defer(deferred::ABANDON, &mut after, &FileHooks { io: self.io });
after.run();
}
}
}