wireshift-core 0.1.1

Core typed operations, buffers, and backend traits for wireshift
Documentation
//! Configures accumulation across multiple ops prior to pushing to kernel.
use std::time::Duration;

use crate::completion::CompletionEvent;
use crate::error::{Error, Result};
use crate::{BackendFactory, Ring};

/// Drains up to `limit` completions in one call.
///
/// ```no_run
/// use wireshift::{Ring, RingConfig, strategy::BatchDrain};
///
/// let ring = Ring::new(RingConfig::default())?;
/// let drain = BatchDrain::new(ring, 16);
/// let _ = drain;
/// # Ok::<(), Box<dyn std::error::Error>>(())
/// ```
#[derive(Clone)]
pub struct BatchDrain<F: BackendFactory> {
    ring: Ring<F>,
    limit: usize,
}

impl<F: BackendFactory> BatchDrain<F> {
    /// Creates a batch drain adapter.
    #[must_use]
    pub fn new(ring: Ring<F>, limit: usize) -> Self {
        Self {
            ring,
            limit: limit.max(1),
        }
    }

    /// Attempts to drain up to `limit` completions within the optional timeout budget.
    #[must_use]
    pub fn drain(&self, timeout: Option<Duration>) -> Vec<Result<CompletionEvent>> {
        let mut completions = Vec::with_capacity(self.limit);
        for index in 0..self.limit {
            // First completion: use caller's timeout (may block).
            // Subsequent: zero-wait poll. At internet scale doing millions of
            // completions per second, even 1ms sleep between completions is
            // catastrophic. We drain everything that's ready, then return.
            let wait = if index == 0 {
                timeout
            } else {
                Some(Duration::ZERO)
            };
            match self.ring.complete(wait) {
                Ok(event) => completions.push(Ok(event)),
                // Non-first completion timeout is EXPECTED - it just means no more
                // completions are ready right now, so stop draining quietly.
                Err(Error::Timeout { .. }) if index > 0 => break,
                // Anything else - a first-completion error, or a non-timeout fatal
                // backend error on a later completion - must be surfaced, not
                // silently broken out of (Law 10). The old code discarded every
                // non-first error, hiding fatal backend failures mid-batch.
                Err(error) => {
                    completions.push(Err(error));
                    break;
                }
            }
        }
        completions
    }
}