use std::{
error::Error,
fmt, ptr,
sync::atomic::{AtomicU32, Ordering, fence},
};
use super::{
sqe::Sqe,
sys::{
IORING_SQ_CQ_OVERFLOW, IORING_SQ_NEED_WAKEUP, IORING_SQ_TASKRUN, IoCqringOffsets,
IoSqringOffsets, IoUringCqe, IoUringSqe,
},
};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) struct SqFull;
impl fmt::Display for SqFull {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("the submission queue is full")
}
}
impl Error for SqFull {}
#[derive(Debug)]
pub(crate) struct Sq {
khead: *const AtomicU32,
ktail: *const AtomicU32,
kflags: *const AtomicU32,
sqes: *mut IoUringSqe,
mask: u32,
entries: u32,
tail: u32,
}
impl Sq {
pub(crate) unsafe fn new(
ring: *mut u8,
off: &IoSqringOffsets,
sqes: *mut IoUringSqe,
entries: u32,
has_array: bool,
) -> Self {
let at = |o: u32| -> *const AtomicU32 {
unsafe { ring.add(o as usize) }.cast::<AtomicU32>()
};
if has_array {
let array = at(off.array).cast_mut().cast::<u32>();
for i in 0..entries {
unsafe { array.add(i as usize).write(i) };
}
}
let ktail = at(off.tail);
Self {
khead: at(off.head),
ktail,
kflags: at(off.flags),
sqes,
mask: entries - 1,
entries,
tail: unsafe { &*ktail }.load(Ordering::Relaxed),
}
}
fn head(&self) -> &AtomicU32 {
unsafe { &*self.khead }
}
fn flags(&self) -> &AtomicU32 {
unsafe { &*self.kflags }
}
pub(crate) fn ready(&self) -> u32 {
self.tail.wrapping_sub(self.head().load(Ordering::Acquire))
}
pub(crate) fn pending(&self, mut f: impl FnMut(u64)) {
let head = self.head().load(Ordering::Acquire);
for at in 0..self.tail.wrapping_sub(head) {
f(unsafe { (*self.sqes.add((head.wrapping_add(at) & self.mask) as usize)).user_data });
}
}
pub(crate) fn space(&self) -> u32 {
self.entries - self.ready()
}
pub(crate) fn push(&mut self, sqe: &Sqe) -> Result<(), SqFull> {
if self.ready() >= self.entries {
return Err(SqFull);
}
unsafe { ptr::write(self.sqes.add((self.tail & self.mask) as usize), sqe.0) };
self.tail = self.tail.wrapping_add(1);
Ok(())
}
pub(crate) fn publish(&mut self) {
unsafe { &*self.ktail }.store(self.tail, Ordering::Release);
}
pub(crate) fn taskrun_pending(&self) -> bool {
self.flags().load(Ordering::Relaxed) & IORING_SQ_TASKRUN != 0
}
pub(crate) fn sqpoll_needs_wakeup(&self) -> bool {
fence(Ordering::SeqCst);
self.sqpoll_asleep()
}
pub(crate) fn cq_overflow_pending(&self) -> bool {
self.flags().load(Ordering::Relaxed) & IORING_SQ_CQ_OVERFLOW != 0
}
pub(crate) fn sqpoll_asleep(&self) -> bool {
self.flags().load(Ordering::Relaxed) & IORING_SQ_NEED_WAKEUP != 0
}
}
#[derive(Debug)]
pub(crate) struct Cq {
khead: *const AtomicU32,
ktail: *const AtomicU32,
kdropped: *const AtomicU32,
cqes: *const IoUringCqe,
mask: u32,
}
impl Cq {
pub(crate) unsafe fn new(ring: *mut u8, off: &IoCqringOffsets, entries: u32) -> Self {
let at = |o: u32| -> *const AtomicU32 {
unsafe { ring.add(o as usize) }.cast::<AtomicU32>()
};
Self {
khead: at(off.head),
ktail: at(off.tail),
kdropped: at(off.overflow),
cqes: unsafe { ring.add(off.cqes as usize) }.cast::<IoUringCqe>(),
mask: entries - 1,
}
}
pub(crate) fn dropped(&self) -> u32 {
unsafe { &*self.kdropped }.load(Ordering::Relaxed)
}
pub(crate) fn completions(&mut self) -> Completions<'_> {
let (khead, ktail) = unsafe { (&*self.khead, &*self.ktail) };
let head = khead.load(Ordering::Relaxed);
let tail = ktail.load(Ordering::Acquire);
Completions {
cq: self,
start: head,
head,
tail,
}
}
}
#[derive(Debug)]
pub(crate) struct Completions<'a> {
cq: &'a Cq,
start: u32,
head: u32,
tail: u32,
}
impl Iterator for Completions<'_> {
type Item = IoUringCqe;
fn next(&mut self) -> Option<IoUringCqe> {
if self.head == self.tail {
return None;
}
let cqe = unsafe { ptr::read(self.cq.cqes.add((self.head & self.cq.mask) as usize)) };
self.head = self.head.wrapping_add(1);
Some(cqe)
}
fn size_hint(&self) -> (usize, Option<usize>) {
let n = self.tail.wrapping_sub(self.head) as usize;
(n, Some(n))
}
}
impl ExactSizeIterator for Completions<'_> {}
impl Drop for Completions<'_> {
fn drop(&mut self) {
if self.head != self.start {
unsafe { &*self.cq.khead }.store(self.head, Ordering::Release);
}
}
}