#![cfg(all(feature = "net", target_os = "linux"))]
use std::io;
use std::ptr;
use crate::buf::{BufferPool, PooledBuf, SetLen};
use crate::driver::backends::iouring::{Submission as UringSubmission, UringOperation};
use crate::driver::helpers::io_handle::SharedIoHandle;
use crate::driver::ops::{Completion, Op};
use crate::runtime::local::CURRENT_DRIVER;
pub(crate) struct RecvManaged {
#[allow(unused)]
io_handle: SharedIoHandle<socket2::Socket>,
pool: BufferPool,
len: u32,
buffer_group: u16,
}
impl Op<RecvManaged> {
pub(crate) fn recv_managed(io_handle: &SharedIoHandle<socket2::Socket>, len: usize) -> io::Result<Op<RecvManaged>> {
let pool = CURRENT_DRIVER.with(|h| h.upgrade().expect("Not in a runtime context").buffer_pool())?;
let buffer_group = pool.buffer_group()?;
let pool_len = pool.buffer_len()?;
let len = if len == 0 { pool_len } else { len.min(pool_len) };
let len = u32::try_from(len)
.map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "managed recv length too large"))?;
let data = RecvManaged {
io_handle: io_handle.clone(),
pool,
len,
buffer_group,
};
CURRENT_DRIVER.with(|handle| handle.upgrade().expect("Not in a runtime context").submit_op(data))
}
}
unsafe impl UringOperation for RecvManaged {
type Output = io::Result<Option<PooledBuf>>;
fn submit(&mut self) -> UringSubmission {
use io_uring::{opcode, squeue::Flags, types};
opcode::Recv::new(types::Fd(self.io_handle.raw_fd()), ptr::null_mut(), self.len)
.buf_group(self.buffer_group)
.build()
.flags(Flags::BUFFER_SELECT)
}
fn complete(self, completion: Completion) -> Self::Output {
let n = match completion.result {
Ok(n) => n as usize,
Err(err) => {
if let Some(bid) = io_uring::cqueue::buffer_select(completion.flags) {
self.pool.recycle_selected(bid);
}
return Err(err);
}
};
let Some(bid) = io_uring::cqueue::buffer_select(completion.flags) else {
if n > 0 {
return Err(io::Error::other("managed recv completed without a buffer id"));
}
return Ok(None);
};
let mut buf = self
.pool
.take(bid)?
.ok_or_else(|| io::Error::other("managed recv buffer id was not available in the pool"))?;
unsafe { buf.set_len(n) };
Ok(Some(buf))
}
}