172 lines
5.0 KiB
Rust
172 lines
5.0 KiB
Rust
use std::{
|
|
io, mem,
|
|
ops::DerefMut as _,
|
|
pin::Pin,
|
|
ptr,
|
|
rc::Rc,
|
|
task::{Context, LocalWaker, Poll},
|
|
};
|
|
|
|
use io_uring::{opcode::AsyncCancel, squeue::Entry};
|
|
use log::trace;
|
|
use pin_project::pin_project;
|
|
use unchecked_refcell::UncheckedRefCell;
|
|
|
|
use crate::{RT, Runtime};
|
|
|
|
#[derive(Default)]
|
|
pub(crate) struct RingOp(RingOpHandle);
|
|
|
|
#[derive(Default, Clone)]
|
|
pub(crate) struct RingOpHandle(Rc<UncheckedRefCell<Inner>>);
|
|
|
|
#[derive(Default)]
|
|
enum Inner {
|
|
#[default]
|
|
New,
|
|
Live(LocalWaker),
|
|
Complete(Complete),
|
|
Cancelled,
|
|
}
|
|
|
|
#[derive(Clone, Copy)]
|
|
pub(crate) struct Complete {
|
|
pub result: i32,
|
|
pub _flags: u32,
|
|
}
|
|
|
|
impl Drop for RingOp {
|
|
fn drop(&mut self) {
|
|
// TODO handle already completed before submitting cancel
|
|
let mut lock = self.0.0.borrow_mut();
|
|
match mem::replace(lock.deref_mut(), Inner::Cancelled) {
|
|
Inner::New | Inner::Complete(_) => {}
|
|
Inner::Cancelled => unreachable!(),
|
|
Inner::Live(_) => RT.with(|rt| {
|
|
let outer = rt.borrow();
|
|
let lock = outer
|
|
.as_ref()
|
|
.expect("future cancelled outside runtime thread")
|
|
.0
|
|
.borrow_mut();
|
|
let entry = AsyncCancel::new(self.0.0.as_ptr().addr().try_into().unwrap()).build();
|
|
trace!("Canceling sqe {:?}", &entry);
|
|
let mut sq = unsafe { lock.ring.submission_shared() };
|
|
unsafe { sq.push(&entry) }.expect("failed to submit cancel");
|
|
// TODO check for full
|
|
}),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl RingOp {
|
|
pub fn poll_submit(
|
|
self: Pin<&mut Self>,
|
|
cx: &mut Context<'_>,
|
|
with_entry: impl FnOnce() -> Entry,
|
|
) -> Poll<Complete> {
|
|
let mut lock = self.0.0.borrow_mut();
|
|
match &mut *lock {
|
|
Inner::Cancelled => unreachable!(),
|
|
Inner::Complete(complete) => Poll::Ready(*complete),
|
|
op @ Inner::New => {
|
|
{
|
|
let rt: &Runtime = cx.ext().downcast_ref().unwrap(); // TODO Get this way or thread local?
|
|
let mut lock = rt.0.borrow_mut();
|
|
let entry = with_entry().user_data(self.0.clone().into_user_data());
|
|
trace!("Pushing SQE {:?}", &entry);
|
|
let mut sq = lock.ring.submission();
|
|
unsafe { sq.push(&entry) }.expect("failed to submit no-op");
|
|
// TODO check for full
|
|
}
|
|
*op = Inner::Live(cx.local_waker().clone());
|
|
Poll::Pending
|
|
}
|
|
Inner::Live(waker) => {
|
|
waker.clone_from(cx.local_waker());
|
|
Poll::Pending
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
impl RingOpHandle {
|
|
pub fn into_user_data(self) -> u64 {
|
|
Rc::into_raw(self.0).expose_provenance().try_into().unwrap()
|
|
}
|
|
|
|
pub unsafe fn from_user_data(user_data: u64) -> Self {
|
|
let ptr = ptr::with_exposed_provenance(user_data.try_into().unwrap());
|
|
Self(unsafe { Rc::from_raw(ptr) })
|
|
}
|
|
|
|
pub fn complete(self, result: i32, flags: u32) {
|
|
// TODO don't drop self for multishot accepts
|
|
let mut lock = self.0.borrow_mut();
|
|
|
|
match mem::replace(
|
|
lock.deref_mut(),
|
|
Inner::Complete(Complete {
|
|
result,
|
|
_flags: flags,
|
|
}),
|
|
) {
|
|
Inner::New | Inner::Complete { .. } => unreachable!(),
|
|
Inner::Cancelled => {}
|
|
Inner::Live(waker) => waker.wake(),
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn submit_and_forget(entry: Entry) {
|
|
RT.with(|rt| {
|
|
let outer = rt.borrow();
|
|
let lock = outer
|
|
.as_ref()
|
|
.expect("socket closed outside runtime thread")
|
|
.0
|
|
.borrow_mut();
|
|
|
|
// Start cancelled to ignore result
|
|
let handle = RingOpHandle(Rc::new(UncheckedRefCell::new(Inner::Cancelled)));
|
|
let entry = entry.user_data(handle.into_user_data());
|
|
trace!("Submitting and forgetting {:?}", &entry);
|
|
let mut sq = unsafe { lock.ring.submission_shared() };
|
|
unsafe { sq.push(&entry) }.expect("failed to submit cancel");
|
|
// TODO check for full
|
|
});
|
|
}
|
|
|
|
#[pin_project]
|
|
struct RingOpFut<E: FnMut() -> Entry> {
|
|
#[pin]
|
|
ring_op: RingOp,
|
|
with_entry: E,
|
|
}
|
|
|
|
impl<E: FnMut() -> Entry> Future for RingOpFut<E> {
|
|
type Output = Complete;
|
|
|
|
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
|
let this = self.project();
|
|
this.ring_op.poll_submit(cx, this.with_entry)
|
|
}
|
|
}
|
|
|
|
pub async fn ring_op(with_entry: impl FnMut() -> Entry) -> Complete {
|
|
RingOpFut {
|
|
ring_op: Default::default(),
|
|
with_entry,
|
|
}
|
|
.await
|
|
}
|
|
|
|
pub async fn ring_op_result(with_entry: impl FnMut() -> Entry) -> io::Result<i32> {
|
|
let Complete { result, .. } = ring_op(with_entry).await;
|
|
if result < 0 {
|
|
Err(io::Error::from_raw_os_error(-result))
|
|
} else {
|
|
Ok(result)
|
|
}
|
|
}
|