use super::{const_buf, mut_buf, Fd};
use crate::runtime::{sched_from_ctx, task_from_ctx, worker_from_ctx, TaskRef};
use crate::{
event::{Event, Scheduler, POLLET, POLLIN, POLLOUT},
Error, Result,
};
use core::future::Future;
use core::ops::Deref;
use core::pin::Pin;
use core::ptr::{self, NonNull};
use core::task::{Context, Poll};
use hioff::container_of_mut;
#[repr(C)]
pub struct AioFd<'a> {
pub(crate) fd: &'a Fd,
event: Event,
events: u32,
task: Option<TaskRef>,
waker: Option<TaskRef>,
sched: Option<NonNull<Scheduler>>,
}
unsafe impl Send for AioFd<'_> {}
unsafe impl Sync for AioFd<'_> {}
impl<'a> AioFd<'a> {
pub fn new(fd: &'a Fd) -> Self {
Self {
fd,
event: Event::new(Self::event_handle),
task: None,
waker: None,
sched: None,
events: 0,
}
}
}
impl AioFd<'_> {
pub async fn wait(&mut self, events: u32) -> Result<()> {
FdWait::new(self, events).await
}
pub async fn read_all(&mut self, mut buf: &mut [u8]) -> Result<usize> {
let mut recved = 0;
loop {
let ret = unsafe { libc::read(self.fd.fd, mut_buf(buf), buf.len()) };
#[allow(clippy::comparison_chain)]
if ret > 0 {
let n = ret as usize;
recved += n;
if n == buf.len() {
return Ok(recved);
}
buf = &mut buf[n..];
} else if ret == 0 {
return Ok(recved);
} else {
let e = Error::last();
if e.errno == libc::EAGAIN {
self.wait(POLLIN).await?;
} else if e.errno != libc::EINTR {
return Err(e);
}
}
}
}
pub async fn write_all(&mut self, mut buf: &[u8]) -> Result<usize> {
let mut sended = 0;
loop {
let ret = unsafe { libc::write(self.fd.fd, const_buf(buf), buf.len()) };
if ret >= 0 {
let n = ret as usize;
sended += n;
if n == buf.len() {
return Ok(sended);
}
buf = &buf[n..];
} else {
let e = Error::last();
if e.errno == libc::EAGAIN {
self.wait(POLLOUT).await?;
} else if e.errno != libc::EINTR {
return Err(e);
}
}
}
}
pub async fn sendfile_all(&mut self, in_fd: i32, off: usize, count: usize) -> Result<usize> {
let mut off = off as i64;
let mut len = count;
let end = off + count as i64;
loop {
let ret = unsafe { libc::sendfile(self.fd.fd, in_fd, &mut off, len) };
if ret >= 0 {
if off == end {
return Ok(count);
}
len -= ret as usize;
} else {
let e = Error::last();
if e.errno == libc::EAGAIN {
self.wait(POLLOUT).await?;
} else if e.errno != libc::EINTR {
return Err(e);
}
}
}
}
pub async fn sendfile(&mut self, in_fd: i32, off: usize, count: usize) -> Result<usize> {
let mut off = off as i64;
loop {
let ret = unsafe { libc::sendfile(self.fd.fd, in_fd, &mut off, count) };
if ret >= 0 {
return Ok(ret as usize);
} else {
let e = Error::last();
if e.errno == libc::EAGAIN {
self.wait(POLLOUT).await?;
} else if e.errno != libc::EINTR {
return Err(e);
}
}
}
}
pub async fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
loop {
let ret = unsafe { libc::read(self.fd.fd, mut_buf(buf), buf.len()) };
if ret >= 0 {
return Ok(ret as usize);
} else {
let e = Error::last();
if e.errno == libc::EAGAIN {
self.wait(POLLIN).await?;
} else if e.errno != libc::EINTR {
return Err(e);
}
}
}
}
pub async fn write(&mut self, buf: &[u8]) -> Result<usize> {
loop {
let ret = unsafe { libc::write(self.fd.fd, const_buf(buf), buf.len()) };
if ret >= 0 {
return Ok(ret as usize);
} else {
let e = Error::last();
if e.errno == libc::EAGAIN {
self.wait(POLLOUT).await?;
} else if e.errno != libc::EINTR {
return Err(e);
}
}
}
}
}
impl AioFd<'_> {
fn event_handle(e: &Event, _events: u32, sched: &mut Scheduler) {
let this = unsafe { container_of_mut!(e, Self, event) };
if let Some(task) = this.waker.take() {
task.clone().fast_wake(sched);
this.task = Some(task);
}
}
fn set_task(&mut self, ctx: &mut Context<'_>) -> bool {
let current = unsafe { task_from_ctx(ctx).as_mut() };
if let Some(task) = self.task.take() {
if ptr::eq(&*task, current) {
self.waker = Some(task);
return false;
}
self.del_event();
self.events = 0;
}
let worker = unsafe { worker_from_ctx(ctx).as_ref() };
current.status.set_local(worker.worker_id());
current.inc_ref();
self.waker = Some(unsafe { TaskRef::from(current) });
true
}
fn add(&mut self, events: u32, ctx: &mut Context<'_>) -> Poll<Result<()>> {
let first = self.set_task(ctx);
if self.events == events {
return Poll::Pending;
}
let sched = unsafe { sched_from_ctx(ctx).as_ref() };
let ret = if !first {
unsafe { sched.mod_fd_event(&self.event, events | POLLET, self.fd.fd) }
} else {
let ret = unsafe { sched.add_fd_event(&self.event, events | POLLET, self.fd.fd) };
if ret.is_ok() {
self.sched = Some(NonNull::from(sched));
}
ret
};
match ret {
Ok(_) => {
self.events = events;
Poll::Pending
}
Err(e) => Poll::Ready(Err(e)),
}
}
fn del_event(&mut self) {
if let Some(sched) = self.sched.take() {
let _ = unsafe { sched.as_ref().del_fd_event(self.fd.fd) };
}
}
}
impl Drop for AioFd<'_> {
fn drop(&mut self) {
self.del_event();
}
}
impl Deref for AioFd<'_> {
type Target = Fd;
fn deref(&self) -> &Self::Target {
self.fd
}
}
struct FdWait<'a, 'b> {
aio: &'a mut AioFd<'b>,
events: u32,
}
impl<'a, 'b> FdWait<'a, 'b> {
fn new(aio: &'a mut AioFd<'b>, events: u32) -> Self {
Self { aio, events }
}
}
impl Future for FdWait<'_, '_> {
type Output = Result<()>;
fn poll(mut self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Output> {
if self.events > 0 {
let events = self.events;
self.events = 0;
self.aio.add(events, ctx)
} else {
Poll::Ready(Ok(()))
}
}
}