use std::any::Any;
use std::future::Future;
use std::marker::PhantomData;
use std::mem;
use std::pin::Pin;
use std::task::{Context, Poll};
use io_uring::squeue;
use crate::runtime;
pub(crate) struct Op<T: 'static> {
sqe: Option<squeue::Entry>,
key: Option<usize>,
data: Option<T>,
worker_uid: usize,
_not_send: PhantomData<*const ()>,
}
impl<T: 'static> Unpin for Op<T> {}
impl<T: 'static> Op<T> {
pub(crate) fn new(sqe: squeue::Entry, data: T) -> Self {
Self {
sqe: Some(sqe),
key: None,
data: Some(data),
worker_uid: 0,
_not_send: PhantomData,
}
}
}
impl<T: 'static> Future for Op<T> {
type Output = (i32, u32, T);
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = &mut *self;
runtime::with_worker(|w| {
let mut driver = w.driver.borrow_mut();
match this.key {
None => {
let sqe = this
.sqe
.take()
.expect("giong: operation polled after completion");
this.key = Some(driver.submit_op(sqe, cx.waker()));
this.worker_uid = w.shared.uid;
Poll::Pending
}
Some(key) => {
assert_eq!(
this.worker_uid, w.shared.uid,
"giong: operation polled on a different runtime"
);
match driver.poll_op(key, cx.waker()) {
Poll::Ready((res, flags)) => {
this.key = None;
let data = this.data.take().expect("operation resources");
Poll::Ready((res, flags, data))
}
Poll::Pending => Poll::Pending,
}
}
}
})
}
}
impl<T: 'static> Drop for Op<T> {
fn drop(&mut self) {
let Some(key) = self.key.take() else { return };
let mut data = self.data.take();
let handed_over = runtime::try_with_worker(|w| {
if w.shared.uid != self.worker_uid {
return false;
}
let Ok(mut driver) = w.driver.try_borrow_mut() else {
return false;
};
driver.cancel_op(key, data.take().map(|d| Box::new(d) as Box<dyn Any>));
true
})
.unwrap_or(false);
if !handed_over {
mem::forget(data);
}
}
}