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
79impl IoUringError {
80    /// Create an operation failed error from an error code.
81    #[must_use]
82    pub fn from_errno(message: impl Into<String>, errno: i32) -> Self {
83        Self::OperationFailed {
84            message: message.into(),
85            errno,
86        }
87    }
88
89    /// Check if this error indicates the ring should be recreated.
90    #[must_use]
91    pub const fn is_fatal(&self) -> bool {
92        matches!(
93            self,
94            Self::RingCreation(_)
95                | Self::BufferRegistration(_)
96                | Self::FdRegistration(_)
97                | Self::RingClosed
98                | Self::NotAvailable
99        )
100    }
101
102    /// Check if this error indicates a transient condition.
103    #[must_use]
104    pub const fn is_transient(&self) -> bool {
105        matches!(self, Self::SubmissionQueueFull | Self::BufferPoolExhausted)
106    }
107}
108
109#[cfg(test)]
110mod tests {
111    use super::*;
112
113    #[test]
114    fn test_error_display() {
115        let err = IoUringError::BufferPoolExhausted;
116        assert_eq!(err.to_string(), "Buffer pool exhausted");
117
118        let err = IoUringError::from_errno("read failed", -5);
119        assert!(err.to_string().contains("read failed"));
120        assert!(err.to_string().contains("-5"));
121    }
122
123    #[test]
124    fn test_is_fatal() {
125        assert!(IoUringError::NotAvailable.is_fatal());
126        assert!(IoUringError::RingClosed.is_fatal());
127        assert!(!IoUringError::BufferPoolExhausted.is_fatal());
128        assert!(!IoUringError::SubmissionQueueFull.is_fatal());
129    }
130
131    #[test]
132    fn test_is_transient() {
133        assert!(IoUringError::SubmissionQueueFull.is_transient());
134        assert!(IoUringError::BufferPoolExhausted.is_transient());
135        assert!(!IoUringError::NotAvailable.is_transient());
136        assert!(!IoUringError::RingClosed.is_transient());
137    }
138}