use std::task::Waker;
use crate::buf::IoBufMut;
pub trait ErasedBuf: Send {
fn on_ignored_completion(&mut self, result: i32) {
let _ = result;
}
}
impl<B: IoBufMut + Send> ErasedBuf for B {}
pub struct PlaceholderBuf;
unsafe impl Send for PlaceholderBuf {}
impl ErasedBuf for PlaceholderBuf {}
pub(crate) struct ErasedBox<T: Send + 'static>(pub(crate) T);
unsafe impl<T: Send + 'static> Send for ErasedBox<T> {}
impl<T: Send + 'static> ErasedBuf for ErasedBox<T> {}
pub struct CloseOnDropFd;
unsafe impl Send for CloseOnDropFd {}
impl ErasedBuf for CloseOnDropFd {
fn on_ignored_completion(&mut self, result: i32) {
if result >= 0 {
unsafe {
libc::close(result as libc::c_int);
}
}
}
}
pub struct SockAddrStorage {
pub storage: libc::sockaddr_storage,
pub len: libc::socklen_t,
}
unsafe impl Send for SockAddrStorage {}
impl ErasedBuf for SockAddrStorage {}
pub(crate) struct IovecsStorage(pub(crate) Vec<libc::iovec>);
unsafe impl Send for IovecsStorage {}
impl ErasedBuf for IovecsStorage {}
pub struct OpenAtData {
pub path: std::ffi::CString,
}
unsafe impl Send for OpenAtData {}
impl ErasedBuf for OpenAtData {
fn on_ignored_completion(&mut self, result: i32) {
if result >= 0 {
unsafe {
libc::close(result as libc::c_int);
}
}
}
}
pub struct MsgHdrData {
pub storage: libc::sockaddr_storage,
pub namelen: libc::socklen_t,
pub iov: libc::iovec,
pub msg: libc::msghdr,
}
unsafe impl Send for MsgHdrData {}
impl ErasedBuf for MsgHdrData {}
impl MsgHdrData {
pub fn new_send(
storage: libc::sockaddr_storage,
namelen: libc::socklen_t,
ptr: *const u8,
len: usize,
) -> Box<Self> {
let mut data = Box::new(Self {
storage,
namelen,
iov: libc::iovec {
iov_base: ptr as *mut libc::c_void,
iov_len: len,
},
msg: unsafe { std::mem::zeroed() },
});
if data.namelen > 0 {
data.msg.msg_name = &mut data.storage as *mut _ as *mut libc::c_void;
data.msg.msg_namelen = data.namelen;
} else {
data.msg.msg_name = std::ptr::null_mut();
data.msg.msg_namelen = 0;
}
data.msg.msg_iov = &mut data.iov as *mut _;
data.msg.msg_iovlen = 1;
data
}
pub fn new_recv(ptr: *mut u8, len: usize) -> Box<Self> {
let mut data = Box::new(Self {
storage: unsafe { std::mem::zeroed() },
namelen: std::mem::size_of::<libc::sockaddr_storage>() as libc::socklen_t,
iov: libc::iovec {
iov_base: ptr as *mut libc::c_void,
iov_len: len,
},
msg: unsafe { std::mem::zeroed() },
});
data.msg.msg_name = &mut data.storage as *mut _ as *mut libc::c_void;
data.msg.msg_namelen = data.namelen;
data.msg.msg_iov = &mut data.iov as *mut _;
data.msg.msg_iovlen = 1;
data
}
pub fn as_msghdr_ptr(&self) -> *const libc::msghdr {
&self.msg as *const _
}
pub fn as_msghdr_mut_ptr(&mut self) -> *mut libc::msghdr {
&mut self.msg as *mut _
}
}
pub enum OpState {
Submitted {
waker: Option<Waker>,
buf: Option<Box<dyn ErasedBuf>>,
},
Complete {
result: i32,
},
Ignored {
buf: Box<dyn ErasedBuf>,
aux: Option<Box<dyn ErasedBuf>>,
},
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct SlabKey(u64);
impl SlabKey {
pub fn new(index: usize, generation: u32) -> Self {
assert!(index <= u32::MAX as usize, "slab index overflow");
SlabKey(((generation as u64) << 32) | (index as u64))
}
pub fn index(self) -> usize {
(self.0 & 0xFFFF_FFFF) as usize
}
pub fn generation(self) -> u32 {
(self.0 >> 32) as u32
}
pub fn as_u64(self) -> u64 {
self.0
}
pub fn from_u64(v: u64) -> Self {
SlabKey(v)
}
}
const NO_FREE: usize = usize::MAX;
enum SlotState {
Vacant { next_free: usize },
Occupied(OpState),
}
struct Slot {
generation: u32,
state: SlotState,
}
pub struct CompletionSlab {
slots: Vec<Slot>,
free_head: usize,
len: usize,
}
impl Default for CompletionSlab {
fn default() -> Self {
Self::new()
}
}
impl CompletionSlab {
pub fn new() -> Self {
Self::with_capacity(crate::profile::HardwareProfile::get().slab_capacity())
}
pub fn with_capacity(capacity: usize) -> Self {
Self {
slots: Vec::with_capacity(capacity),
free_head: NO_FREE,
len: 0,
}
}
#[inline]
fn get_occupied_mut(&mut self, index: usize, generation: u32) -> Option<&mut OpState> {
let slot = self.slots.get_mut(index)?;
if slot.generation != generation {
return None;
}
match &mut slot.state {
SlotState::Occupied(op) => Some(op),
SlotState::Vacant { .. } => None,
}
}
#[inline]
fn free_slot(&mut self, index: usize) {
let slot = &mut self.slots[index];
slot.generation = slot.generation.wrapping_add(1);
slot.state = SlotState::Vacant {
next_free: self.free_head,
};
self.free_head = index;
self.len -= 1;
}
#[inline]
pub fn insert(&mut self, state: OpState) -> SlabKey {
self.len += 1;
if self.free_head != NO_FREE {
let index = self.free_head;
let slot = &mut self.slots[index];
let next_free = match slot.state {
SlotState::Vacant { next_free } => next_free,
SlotState::Occupied(_) => unreachable!("free list pointed to occupied slot"),
};
self.free_head = next_free;
slot.state = SlotState::Occupied(state);
SlabKey::new(index, slot.generation)
} else {
let index = self.slots.len();
self.slots.push(Slot {
generation: 0,
state: SlotState::Occupied(state),
});
SlabKey::new(index, 0)
}
}
#[inline]
pub fn update_waker(&mut self, key: SlabKey, waker: Waker) {
if let Some(OpState::Submitted {
waker: ref mut w, ..
}) = self.get_occupied_mut(key.index(), key.generation())
{
*w = Some(waker);
}
}
#[inline]
pub fn poll_result(&mut self, key: SlabKey, waker: &Waker) -> Option<i32> {
let op = self.get_occupied_mut(key.index(), key.generation())?;
match op {
OpState::Complete { result } => Some(*result),
OpState::Submitted {
waker: ref mut w, ..
} => {
if !matches!(w, Some(existing) if existing.will_wake(waker)) {
*w = Some(waker.clone());
}
None
}
OpState::Ignored { .. } => None,
}
}
#[inline]
pub fn poll_and_consume(&mut self, key: SlabKey, waker: &Waker) -> Option<i32> {
let index = key.index();
let op = self.get_occupied_mut(index, key.generation())?;
match op {
OpState::Complete { result } => {
let res = *result;
self.free_slot(index);
Some(res)
}
OpState::Submitted {
waker: ref mut w, ..
} => {
if !matches!(w, Some(existing) if existing.will_wake(waker)) {
*w = Some(waker.clone());
}
None
}
OpState::Ignored { .. } => None,
}
}
#[inline]
pub fn complete(&mut self, user_data: u64, result: i32) -> Option<Waker> {
let key = SlabKey::from_u64(user_data);
let index = key.index();
let op = self.get_occupied_mut(index, key.generation())?;
let prev = std::mem::replace(op, OpState::Complete { result });
match prev {
OpState::Submitted { waker, buf: _ } => waker,
OpState::Ignored { mut buf, mut aux } => {
buf.on_ignored_completion(result);
if let Some(ref mut a) = aux {
a.on_ignored_completion(result);
}
drop(buf);
drop(aux);
self.free_slot(index);
None
}
OpState::Complete { .. } => None,
}
}
pub fn ignore(&mut self, key: SlabKey, buf: Box<dyn ErasedBuf>) -> bool {
let index = key.index();
if let Some(op) = self.get_occupied_mut(index, key.generation()) {
match op {
OpState::Submitted {
buf: ref mut prev_buf,
..
} => {
let old_buf = prev_buf.take();
*op = OpState::Ignored { buf, aux: old_buf };
return true;
}
OpState::Complete { .. } => {
drop(buf);
self.free_slot(index);
return false;
}
OpState::Ignored { .. } => {
drop(buf);
return false;
}
}
}
drop(buf);
false
}
pub fn ignore_simple(&mut self, key: SlabKey) -> bool {
self.ignore_simple_inner(key, false)
}
pub fn ignore_accept(&mut self, key: SlabKey) -> bool {
self.ignore_simple_inner(key, true)
}
fn ignore_simple_inner(&mut self, key: SlabKey, close_fd_on_complete: bool) -> bool {
let index = key.index();
if let Some(op) = self.get_occupied_mut(index, key.generation()) {
let state = std::mem::replace(op, OpState::Complete { result: 0 });
match state {
OpState::Submitted { buf, .. } => {
*op = OpState::Ignored {
buf: buf.unwrap_or_else(|| Box::new(PlaceholderBuf)),
aux: None,
};
return true;
}
OpState::Complete { result } => {
if close_fd_on_complete && result >= 0 {
unsafe {
libc::close(result as libc::c_int);
}
}
self.free_slot(index);
return false;
}
OpState::Ignored { buf, aux } => {
*op = OpState::Ignored { buf, aux };
return false;
}
}
}
false
}
#[inline]
pub fn consume(&mut self, key: SlabKey) {
let index = key.index();
if let Some(OpState::Complete { .. }) = self.get_occupied_mut(index, key.generation()) {
self.free_slot(index);
}
}
pub(crate) fn remove_unsubmitted(&mut self, key: SlabKey) {
let index = key.index();
if let Some(OpState::Submitted { .. }) = self.get_occupied_mut(index, key.generation()) {
self.free_slot(index);
}
}
#[inline]
pub fn len(&self) -> usize {
self.len
}
#[inline]
pub fn is_empty(&self) -> bool {
self.len == 0
}
}