use std::collections::HashMap;
use std::pin::Pin;
#[cfg(test)]
use std::sync::Arc;
use std::sync::{Mutex, Weak};
use std::task::{Context, Poll};
use crate::error::{Result, SaferRingError};
use crate::ownership::OwnedBuffer;
pub type SubmissionId = u64;
pub trait CompletionChecker: Sync {
fn try_complete_safe_operation(
&self,
submission_id: SubmissionId,
) -> Result<Option<std::io::Result<i32>>>;
fn supports_async_wait(&self) -> bool {
false }
}
pub struct SafeOperation {
submission_id: SubmissionId,
buffer: Option<OwnedBuffer>,
orphan_tracker: Weak<Mutex<OrphanTracker>>,
completed: bool,
}
impl SafeOperation {
pub fn new(
buffer: OwnedBuffer,
submission_id: SubmissionId,
orphan_tracker: Weak<Mutex<OrphanTracker>>,
) -> Self {
Self {
submission_id,
buffer: Some(buffer),
orphan_tracker,
completed: false,
}
}
pub fn failed(
buffer: OwnedBuffer,
submission_id: SubmissionId,
orphan_tracker: Weak<Mutex<OrphanTracker>>,
) -> Self {
Self {
submission_id,
buffer: Some(buffer),
orphan_tracker,
completed: true, }
}
pub fn submission_id(&self) -> SubmissionId {
self.submission_id
}
pub fn is_completed(&self) -> bool {
self.completed
}
pub fn complete(mut self) -> Result<OwnedBuffer> {
self.completed = true;
self.buffer.take().ok_or_else(|| {
SaferRingError::Io(std::io::Error::other("Operation buffer already taken"))
})
}
pub(crate) fn into_future<'ring>(
self,
ring: &'ring dyn CompletionChecker,
waker_registry: std::sync::Arc<crate::future::WakerRegistry>,
) -> SafeOperationFuture<'ring> {
SafeOperationFuture {
operation: Some(self),
ring,
waker_registry,
}
}
pub fn buffer_size(&self) -> Option<usize> {
self.buffer.as_ref().map(|b| b.size())
}
pub fn buffer_info(&self) -> Result<(*mut u8, usize)> {
if let Some(buffer) = &self.buffer {
buffer.give_to_kernel(self.submission_id)
} else {
Ok((std::ptr::null_mut(), 0))
}
}
}
impl Drop for SafeOperation {
fn drop(&mut self) {
if !self.completed && self.buffer.is_some() {
if let Some(tracker) = self.orphan_tracker.upgrade() {
if let Ok(mut tracker) = tracker.lock() {
if let Some(buffer) = &self.buffer {
tracker.register_orphan(self.submission_id, buffer.clone_handle());
}
}
}
}
}
}
pub struct SafeOperationFuture<'ring> {
operation: Option<SafeOperation>,
ring: &'ring dyn CompletionChecker,
waker_registry: std::sync::Arc<crate::future::WakerRegistry>,
}
impl<'ring> std::future::Future for SafeOperationFuture<'ring> {
type Output = Result<(usize, OwnedBuffer)>;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
if let Some(mut operation) = self.operation.take() {
if operation.completed && operation.buffer.is_some() {
let _buffer = operation.buffer.take().unwrap();
return Poll::Ready(Err(SaferRingError::Io(std::io::Error::other(
"Operation failed during submission",
))));
}
match self
.ring
.try_complete_safe_operation(operation.submission_id)
{
Ok(Some(completion_result)) => {
operation.completed = true;
let buffer = operation.buffer.take().ok_or_else(|| {
SaferRingError::Io(std::io::Error::other(
"Operation buffer missing after completion",
))
})?;
self.waker_registry.remove_waker(operation.submission_id);
let bytes_transferred = completion_result.map_err(SaferRingError::Io)?;
Poll::Ready(Ok((bytes_transferred as usize, buffer)))
}
Ok(None) => {
let submission_id = operation.submission_id;
self.operation = Some(operation);
self.waker_registry
.register_waker(submission_id, cx.waker().clone());
let waker = cx.waker().clone();
tokio::spawn(async move {
for _ in 0..3 {
tokio::task::yield_now().await;
}
waker.wake();
});
Poll::Pending
}
Err(e) => {
self.waker_registry.remove_waker(operation.submission_id);
Poll::Ready(Err(e))
}
}
} else {
Poll::Ready(Err(SaferRingError::Io(std::io::Error::other(
"Operation future polled after completion",
))))
}
}
}
pub struct SafeAcceptFuture<'ring> {
operation: Option<SafeOperation>,
ring: &'ring dyn CompletionChecker,
waker_registry: std::sync::Arc<crate::future::WakerRegistry>,
}
impl<'ring> SafeAcceptFuture<'ring> {
pub(crate) fn new(
operation: SafeOperation,
ring: &'ring dyn CompletionChecker,
waker_registry: std::sync::Arc<crate::future::WakerRegistry>,
) -> Self {
Self {
operation: Some(operation),
ring,
waker_registry,
}
}
}
impl<'ring> std::future::Future for SafeAcceptFuture<'ring> {
type Output = Result<(usize, OwnedBuffer)>;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
if let Some(mut operation) = self.operation.take() {
if operation.completed && operation.buffer.is_some() {
let _buffer = operation.buffer.take().unwrap();
return Poll::Ready(Err(SaferRingError::Io(std::io::Error::other(
"Accept operation failed during submission",
))));
}
match self
.ring
.try_complete_safe_operation(operation.submission_id)
{
Ok(Some(completion_result)) => {
operation.completed = true;
let buffer = operation.buffer.take().ok_or_else(|| {
SaferRingError::Io(std::io::Error::other(
"Accept operation buffer missing after completion",
))
})?;
self.waker_registry.remove_waker(operation.submission_id);
let bytes_transferred = completion_result.map_err(SaferRingError::Io)?;
Poll::Ready(Ok((bytes_transferred as usize, buffer)))
}
Ok(None) => {
let submission_id = operation.submission_id;
self.operation = Some(operation);
self.waker_registry
.register_waker(submission_id, cx.waker().clone());
let waker = cx.waker().clone();
tokio::spawn(async move {
for _ in 0..3 {
tokio::task::yield_now().await;
}
waker.wake();
});
Poll::Pending
}
Err(e) => {
self.waker_registry.remove_waker(operation.submission_id);
Poll::Ready(Err(e))
}
}
} else {
Poll::Ready(Err(SaferRingError::Io(std::io::Error::other(
"Accept future polled after completion",
))))
}
}
}
#[derive(Debug)]
pub struct OrphanTracker {
orphaned_operations: HashMap<SubmissionId, OwnedBuffer>,
next_submission_id: SubmissionId,
}
impl OrphanTracker {
pub fn new() -> Self {
Self {
orphaned_operations: HashMap::new(),
next_submission_id: 1, }
}
pub fn next_submission_id(&mut self) -> SubmissionId {
let id = self.next_submission_id;
self.next_submission_id = self.next_submission_id.wrapping_add(1);
id
}
pub fn register_orphan(&mut self, submission_id: SubmissionId, buffer: OwnedBuffer) {
self.orphaned_operations.insert(submission_id, buffer);
}
pub fn handle_completion(
&mut self,
submission_id: SubmissionId,
result: std::io::Result<i32>,
) -> Option<(OwnedBuffer, std::io::Result<i32>)> {
self.orphaned_operations
.remove(&submission_id)
.map(|buffer| (buffer, result))
}
pub fn orphan_count(&self) -> usize {
self.orphaned_operations.len()
}
pub fn is_orphaned(&self, submission_id: SubmissionId) -> bool {
self.orphaned_operations.contains_key(&submission_id)
}
pub fn cleanup_all_orphans(&mut self) -> usize {
let count = self.orphaned_operations.len();
self.orphaned_operations.clear();
count
}
}
impl Default for OrphanTracker {
fn default() -> Self {
Self::new()
}
}
pub struct SafeOperationBuilder {
buffer: Option<OwnedBuffer>,
submission_id: Option<SubmissionId>,
orphan_tracker: Option<Weak<Mutex<OrphanTracker>>>,
}
impl SafeOperationBuilder {
pub fn new() -> Self {
Self {
buffer: None,
submission_id: None,
orphan_tracker: None,
}
}
pub fn buffer(mut self, buffer: OwnedBuffer) -> Self {
self.buffer = Some(buffer);
self
}
pub fn submission_id(mut self, id: SubmissionId) -> Self {
self.submission_id = Some(id);
self
}
pub fn orphan_tracker(mut self, tracker: Weak<Mutex<OrphanTracker>>) -> Self {
self.orphan_tracker = Some(tracker);
self
}
pub fn build(self) -> Result<SafeOperation> {
let buffer = self.buffer.ok_or_else(|| {
SaferRingError::Io(std::io::Error::new(
std::io::ErrorKind::InvalidInput,
"Buffer is required for safe operation",
))
})?;
let submission_id = self.submission_id.ok_or_else(|| {
SaferRingError::Io(std::io::Error::new(
std::io::ErrorKind::InvalidInput,
"Submission ID is required for safe operation",
))
})?;
let orphan_tracker = self.orphan_tracker.ok_or_else(|| {
SaferRingError::Io(std::io::Error::new(
std::io::ErrorKind::InvalidInput,
"Orphan tracker is required for safe operation",
))
})?;
Ok(SafeOperation::new(buffer, submission_id, orphan_tracker))
}
}
impl Default for SafeOperationBuilder {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_orphan_tracker_creation() {
let tracker = OrphanTracker::new();
assert_eq!(tracker.orphan_count(), 0);
}
#[test]
fn test_submission_id_generation() {
let mut tracker = OrphanTracker::new();
let id1 = tracker.next_submission_id();
let id2 = tracker.next_submission_id();
assert_ne!(id1, id2);
assert_eq!(id1, 1);
assert_eq!(id2, 2);
}
#[test]
fn test_orphan_registration() {
let mut tracker = OrphanTracker::new();
let buffer = OwnedBuffer::new(1024);
let submission_id = 123;
tracker.register_orphan(submission_id, buffer);
assert_eq!(tracker.orphan_count(), 1);
assert!(tracker.is_orphaned(submission_id));
}
#[test]
fn test_orphan_completion_handling() {
let mut tracker = OrphanTracker::new();
let buffer = OwnedBuffer::new(1024);
let submission_id = 123;
tracker.register_orphan(submission_id, buffer);
assert_eq!(tracker.orphan_count(), 1);
let result = Ok(100); let completed = tracker.handle_completion(submission_id, result);
assert!(completed.is_some());
assert_eq!(tracker.orphan_count(), 0);
assert!(!tracker.is_orphaned(submission_id));
if let Some((buffer, result)) = completed {
assert_eq!(buffer.size(), 1024);
assert_eq!(result.unwrap(), 100);
}
}
#[test]
fn test_active_operation_completion() {
let mut tracker = OrphanTracker::new();
let submission_id = 456;
let result = Ok(50);
let completed = tracker.handle_completion(submission_id, result);
assert!(completed.is_none()); }
#[test]
fn test_cleanup_all_orphans() {
let mut tracker = OrphanTracker::new();
for i in 1..=5 {
let buffer = OwnedBuffer::new(1024);
tracker.register_orphan(i, buffer);
}
assert_eq!(tracker.orphan_count(), 5);
let cleaned_up = tracker.cleanup_all_orphans();
assert_eq!(cleaned_up, 5);
assert_eq!(tracker.orphan_count(), 0);
}
#[test]
fn test_safe_operation_creation() {
let tracker = Arc::new(Mutex::new(OrphanTracker::new()));
let buffer = OwnedBuffer::new(1024);
let submission_id = 789;
let operation = SafeOperation::new(buffer, submission_id, Arc::downgrade(&tracker));
assert_eq!(operation.submission_id(), submission_id);
assert!(!operation.is_completed());
assert_eq!(operation.buffer_size(), Some(1024));
}
#[test]
fn test_safe_operation_completion() {
let tracker = Arc::new(Mutex::new(OrphanTracker::new()));
let buffer = OwnedBuffer::new(512);
let submission_id = 999;
let operation = SafeOperation::new(buffer, submission_id, Arc::downgrade(&tracker));
let returned_buffer = operation.complete().unwrap();
assert_eq!(returned_buffer.size(), 512);
let tracker_guard = tracker.lock().unwrap();
assert_eq!(tracker_guard.orphan_count(), 0);
}
#[test]
fn test_safe_operation_drop_registers_orphan() {
let tracker = Arc::new(Mutex::new(OrphanTracker::new()));
let buffer = OwnedBuffer::new(256);
let submission_id = 111;
{
let _operation = SafeOperation::new(buffer, submission_id, Arc::downgrade(&tracker));
}
let tracker_guard = tracker.lock().unwrap();
assert_eq!(tracker_guard.orphan_count(), 1);
assert!(tracker_guard.is_orphaned(submission_id));
}
#[test]
fn test_safe_operation_builder() {
let tracker = Arc::new(Mutex::new(OrphanTracker::new()));
let buffer = OwnedBuffer::new(1024);
let operation = SafeOperationBuilder::new()
.buffer(buffer)
.submission_id(42)
.orphan_tracker(Arc::downgrade(&tracker))
.build()
.unwrap();
assert_eq!(operation.submission_id(), 42);
assert_eq!(operation.buffer_size(), Some(1024));
}
#[test]
fn test_safe_operation_builder_missing_fields() {
let result = SafeOperationBuilder::new()
.submission_id(42)
.build();
assert!(result.is_err());
}
#[test]
fn test_submission_id_wrapping() {
let mut tracker = OrphanTracker::new();
tracker.next_submission_id = u64::MAX;
let id1 = tracker.next_submission_id();
let id2 = tracker.next_submission_id();
assert_eq!(id1, u64::MAX);
assert_eq!(id2, 0); }
}