Skip to main content

laminar_core/io_uring/
error.rs

1//! Error types for `io_uring` operations.
2
3use std::io;
4
5/// Errors that can occur during `io_uring` operations.
6#[derive(Debug, thiserror::Error)]
7pub enum IoUringError {
8    /// Failed to create the `io_uring` ring.
9    #[error("Failed to create io_uring ring: {0}")]
10    RingCreation(#[source] io::Error),
11
12    /// Failed to register buffers with the kernel.
13    #[error("Failed to register buffers: {0}")]
14    BufferRegistration(#[source] io::Error),
15
16    /// Failed to register file descriptors.
17    #[error("Failed to register file descriptors: {0}")]
18    FdRegistration(#[source] io::Error),
19
20    /// Submission queue is full.
21    #[error("Submission queue is full")]
22    SubmissionQueueFull,
23
24    /// No buffers available in the pool.
25    #[error("Buffer pool exhausted")]
26    BufferPoolExhausted,
27
28    /// Invalid buffer index.
29    #[error("Invalid buffer index: {0}")]
30    InvalidBufferIndex(u16),
31
32    /// Operation failed with error code.
33    #[error("I/O operation failed: {message} (error: {errno})")]
34    OperationFailed {
35        /// Error message.
36        message: String,
37        /// Error code from the kernel.
38        errno: i32,
39    },
40
41    /// Submission failed.
42    #[error("Submission failed: {0}")]
43    SubmissionFailed(#[source] io::Error),
44
45    /// Wait for completions failed.
46    #[error("Wait for completions failed: {0}")]
47    WaitFailed(#[source] io::Error),
48
49    /// Invalid configuration.
50    #[error("Invalid configuration: {0}")]
51    InvalidConfig(String),
52
53    /// Feature not supported on this kernel.
54    #[error("Feature not supported: {feature} requires Linux {required_version}")]
55    FeatureNotSupported {
56        /// The feature that is not supported.
57        feature: String,
58        /// The required kernel version.
59        required_version: String,
60    },
61
62    /// `io_uring` not available on this platform.
63    #[error("io_uring is not available on this platform (requires Linux 5.10+)")]
64    NotAvailable,
65
66    /// File descriptor not registered.
67    #[error("File descriptor not registered: {0}")]
68    FdNotRegistered(i32),
69
70    /// Ring already closed.
71    #[error("Ring already closed")]
72    RingClosed,
73
74    /// Pending operation not found.
75    #[error("Pending operation not found: {0}")]
76    PendingNotFound(u64),
77
78    /// Data exceeds buffer capacity.
79    #[error("Buffer too small: need {needed} bytes but buffer is {capacity} bytes")]
80    BufferTooSmall {
81        /// Bytes required.
82        needed: usize,
83        /// Buffer capacity.
84        capacity: usize,
85    },
86}
87
88impl IoUringError {
89    /// Create an operation failed error from an error code.
90    #[must_use]
91    pub fn from_errno(message: impl Into<String>, errno: i32) -> Self {
92        Self::OperationFailed {
93            message: message.into(),
94            errno,
95        }
96    }
97
98    /// Check if this error indicates the ring should be recreated.
99    #[must_use]
100    pub const fn is_fatal(&self) -> bool {
101        matches!(
102            self,
103            Self::RingCreation(_)
104                | Self::BufferRegistration(_)
105                | Self::FdRegistration(_)
106                | Self::RingClosed
107                | Self::NotAvailable
108        )
109    }
110
111    /// Check if this error indicates a transient condition.
112    #[must_use]
113    pub const fn is_transient(&self) -> bool {
114        matches!(self, Self::SubmissionQueueFull | Self::BufferPoolExhausted)
115    }
116}
117
118#[cfg(test)]
119mod tests {
120    use super::*;
121
122    #[test]
123    fn test_error_display() {
124        let err = IoUringError::BufferPoolExhausted;
125        assert_eq!(err.to_string(), "Buffer pool exhausted");
126
127        let err = IoUringError::from_errno("read failed", -5);
128        assert!(err.to_string().contains("read failed"));
129        assert!(err.to_string().contains("-5"));
130    }
131
132    #[test]
133    fn test_is_fatal() {
134        assert!(IoUringError::NotAvailable.is_fatal());
135        assert!(IoUringError::RingClosed.is_fatal());
136        assert!(!IoUringError::BufferPoolExhausted.is_fatal());
137        assert!(!IoUringError::SubmissionQueueFull.is_fatal());
138    }
139
140    #[test]
141    fn test_is_transient() {
142        assert!(IoUringError::SubmissionQueueFull.is_transient());
143        assert!(IoUringError::BufferPoolExhausted.is_transient());
144        assert!(!IoUringError::NotAvailable.is_transient());
145        assert!(!IoUringError::RingClosed.is_transient());
146    }
147}