#![cfg(feature = "io-uring")]
use crate::io_uring_backend::ops::UserData;
use crate::io_uring_backend::send_buffer_pool::RegisteredSendBufferId;
use bytes::Bytes;
use std::collections::HashMap;
use std::os::unix::io::RawFd;
pub(crate) struct PinnedEgressBatch {
pub iovecs: Box<[libc::iovec]>,
pub payloads: Vec<Bytes>,
pub total_len: usize,
pub send_op_flags: i32,
}
unsafe impl Send for PinnedEgressBatch {}
impl std::fmt::Debug for PinnedEgressBatch {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("PinnedEgressBatch")
.field("iovec_count", &self.iovecs.len())
.field("total_len", &self.total_len)
.finish()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub(crate) enum InternalOpType {
Accept,
RingRead,
Send,
SendZeroCopy,
CloseFd,
GenericHandlerOp,
EventFdPoll,
RingReadMultishot,
AsyncCancel,
SendRawVectored,
SendZeroCopyLeased,
}
#[derive(Debug)]
pub(crate) enum InternalOpPayload {
None,
SendBuffer {
buffer: Bytes,
send_op_flags: i32,
app_op_ud: Option<UserData>,
app_op_name: Option<String>,
},
CancelTarget {
target_user_data: UserData,
},
SendZeroCopy {
send_buf_id: RegisteredSendBufferId,
original_data: Bytes,
send_op_flags: i32,
app_op_ud: UserData,
app_op_name: String,
},
RawVectored(PinnedEgressBatch),
SendZeroCopyLeased {
send_buf_id: RegisteredSendBufferId,
},
}
impl Default for InternalOpPayload {
fn default() -> Self {
InternalOpPayload::None
}
}
#[derive(Debug)]
pub(crate) struct InternalOpDetails {
pub fd: RawFd,
pub op_type: InternalOpType,
pub payload: InternalOpPayload,
}
const INTERNAL_OP_BASE: u64 = 1_000_000_000;
#[derive(Debug)]
pub(crate) struct InternalOpTracker {
pub(crate) op_to_details: slab::Slab<InternalOpDetails>,
pending_notifications: HashMap<UserData, InternalOpDetails>,
}
impl InternalOpTracker {
pub fn new() -> Self {
Self {
op_to_details: slab::Slab::new(),
pending_notifications: HashMap::new(),
}
}
pub fn new_op_id(
&mut self,
fd: RawFd,
op_type: InternalOpType,
payload: InternalOpPayload,
) -> UserData {
let key = self.op_to_details.insert(InternalOpDetails {
fd,
op_type,
payload,
});
key as u64 + INTERNAL_OP_BASE
}
pub fn take_op_details(&mut self, user_data: UserData) -> Option<InternalOpDetails> {
if user_data < INTERNAL_OP_BASE {
return None;
}
let key = (user_data - INTERNAL_OP_BASE) as usize;
if self.op_to_details.contains(key) {
Some(self.op_to_details.remove(key))
} else {
self.pending_notifications.remove(&user_data)
}
}
#[allow(dead_code)]
pub fn get_op_details(&self, user_data: UserData) -> Option<&InternalOpDetails> {
if user_data < INTERNAL_OP_BASE {
return None;
}
let key = (user_data - INTERNAL_OP_BASE) as usize;
self
.op_to_details
.get(key)
.or_else(|| self.pending_notifications.get(&user_data))
}
pub fn reinsert_for_notification(&mut self, user_data: UserData, details: InternalOpDetails) {
self.pending_notifications.insert(user_data, details);
}
pub fn is_empty(&self) -> bool {
self.op_to_details.is_empty() && self.pending_notifications.is_empty()
}
pub fn all_op_ids(&self) -> Vec<UserData> {
self
.op_to_details
.iter()
.map(|(k, _)| k as u64 + INTERNAL_OP_BASE)
.chain(self.pending_notifications.keys().copied())
.collect()
}
pub fn remove_ops_for_fd(&mut self, fd_to_remove: RawFd) -> Vec<InternalOpDetails> {
let slab_keys: Vec<usize> = self
.op_to_details
.iter()
.filter(|(_, v)| v.fd == fd_to_remove)
.map(|(k, _)| k)
.collect();
let mut removed: Vec<InternalOpDetails> = slab_keys
.into_iter()
.map(|k| self.op_to_details.remove(k))
.collect();
let notif_keys: Vec<UserData> = self
.pending_notifications
.iter()
.filter(|(_, d)| d.fd == fd_to_remove)
.map(|(k, _)| *k)
.collect();
for k in notif_keys {
if let Some(d) = self.pending_notifications.remove(&k) {
removed.push(d);
}
}
removed
}
pub fn find_ops_for_fd(
&self,
fd_to_find: RawFd,
predicate: impl Fn(InternalOpType) -> bool,
) -> Vec<UserData> {
let mut result: Vec<UserData> = self
.op_to_details
.iter()
.filter(|(_, d)| d.fd == fd_to_find && predicate(d.op_type))
.map(|(k, _)| k as u64 + INTERNAL_OP_BASE)
.collect();
let from_notifs: Vec<UserData> = self
.pending_notifications
.iter()
.filter(|(_, d)| d.fd == fd_to_find && predicate(d.op_type))
.map(|(k, _)| *k)
.collect();
result.extend(from_notifs);
result
}
pub(crate) fn has_pending_read_op(&self, fd_to_check: RawFd) -> bool {
self.op_to_details.iter().any(|(_, d)| {
d.fd == fd_to_check
&& matches!(
d.op_type,
InternalOpType::RingRead | InternalOpType::RingReadMultishot
)
})
}
}