use std::collections::HashSet;
use std::io;
use std::sync::atomic::{AtomicU64, Ordering};
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub(crate) struct RingId(u64);
impl RingId {
pub(crate) fn next() -> Self {
static NEXT: AtomicU64 = AtomicU64::new(1);
Self(NEXT.fetch_add(1, Ordering::Relaxed))
}
}
#[derive(Debug)]
pub(crate) struct Accounting {
ring_id: RingId,
next_user_data: usize,
in_flight: HashSet<usize>,
registered_files: u32,
registered_buffers: u32,
}
impl Accounting {
pub(crate) fn new() -> Self {
Self {
ring_id: RingId::next(),
next_user_data: 0,
in_flight: HashSet::new(),
registered_files: 0,
registered_buffers: 0,
}
}
pub(crate) fn ring_id(&self) -> RingId {
self.ring_id
}
pub(crate) fn outstanding(&self) -> usize {
self.in_flight.len()
}
pub(crate) fn is_quiescent(&self) -> bool {
self.in_flight.is_empty()
}
pub(crate) fn reserve_user_data(&mut self) -> io::Result<usize> {
let id = self.next_user_data;
self.next_user_data = id
.checked_add(1)
.ok_or_else(|| io::Error::other("IoRing operation identity space exhausted"))?;
if !self.in_flight.insert(id) {
return Err(io::Error::other(
"IoRing operation identity is already in flight",
));
}
Ok(id)
}
pub(crate) fn record_completion(&mut self, user_data: usize) -> bool {
self.in_flight.remove(&user_data)
}
pub(crate) fn cancel_reservation(&mut self, user_data: usize) {
let held = self.in_flight.remove(&user_data);
debug_assert!(held, "only a reservation still in flight can be released");
}
pub(crate) fn registered_file_count(&self) -> u32 {
self.registered_files
}
pub(crate) fn registered_buffer_count(&self) -> u32 {
self.registered_buffers
}
pub(crate) fn reserve_registered_files(&mut self, count: u32) {
self.registered_files = self.registered_files.saturating_add(count);
}
pub(crate) fn reserve_registered_buffers(&mut self, count: u32) {
self.registered_buffers = self.registered_buffers.saturating_add(count);
}
#[cfg(test)]
pub(crate) fn set_next_user_data_for_test(&mut self, next: usize) {
self.next_user_data = next;
}
}
#[cfg(test)]
mod tests;