#![cfg(all(feature = "net", target_os = "linux"))]
use std::io;
use crate::buf::{BufferPool, PooledBuf, SetLen};
use crate::driver::backends::iouring::{MultishotCleanup, Submission as UringSubmission, UringMultishotOperation};
use crate::driver::helpers::io_handle::SharedIoHandle;
use crate::driver::ops::{Completion, MultiOp};
use crate::runtime::local::CURRENT_DRIVER;
pub(crate) struct RecvMulti {
io_handle: SharedIoHandle<socket2::Socket>,
pool: BufferPool,
buffer_group: u16,
}
impl MultiOp<RecvMulti> {
pub(crate) fn recv_multi(io_handle: &SharedIoHandle<socket2::Socket>) -> io::Result<Self> {
let pool = CURRENT_DRIVER.with(|h| h.upgrade().expect("Not in a runtime context").buffer_pool())?;
let buffer_group = pool.buffer_group()?;
let data = RecvMulti {
io_handle: io_handle.clone(),
pool,
buffer_group,
};
CURRENT_DRIVER.with(|handle| Ok(handle.upgrade().expect("Not in a runtime context").defer_multi_op(data)))
}
}
fn take_buffer(pool: &BufferPool, completion: &Completion) -> io::Result<Option<PooledBuf>> {
let bid = match io_uring::cqueue::buffer_select(completion.flags) {
Some(bid) => bid,
None => return Ok(None),
};
let mut buf = pool
.take(bid)?
.ok_or_else(|| io::Error::other("multishot recv buffer id was not available in the pool"))?;
if let Ok(n) = completion.result {
unsafe { buf.set_len(n as usize) };
}
Ok(Some(buf))
}
unsafe impl UringMultishotOperation for RecvMulti {
type Item = io::Result<PooledBuf>;
fn submit(&mut self) -> UringSubmission {
use io_uring::{opcode, types};
opcode::RecvMulti::new(types::Fd(self.io_handle.raw_fd()), self.buffer_group).build()
}
fn complete_item(&mut self, completion: Completion) -> Option<Self::Item> {
match completion.result {
Ok(0) => {
match take_buffer(&self.pool, &completion) {
Ok(Some(buf)) => Some(Ok(buf)),
Ok(None) => None,
Err(err) => Some(Err(err)),
}
}
Ok(_) => {
Some(take_buffer(&self.pool, &completion).and_then(|buf| {
buf.ok_or_else(|| io::Error::other("multishot recv completed without a buffer id"))
}))
}
Err(err) => {
if let Some(bid) = io_uring::cqueue::buffer_select(completion.flags) {
self.pool.recycle_selected(bid);
}
Some(Err(err))
}
}
}
fn submission_error(error: io::Error) -> Self::Item {
Err(error)
}
fn completion_cleanup(&self) -> MultishotCleanup {
MultishotCleanup::ProvidedBuffer(self.pool.clone())
}
}