Skip to main content

numaperf_core/
error.rs

1//! Error types for numaperf.
2
3use std::io;
4use thiserror::Error;
5
6/// Unified error type for all numaperf operations.
7///
8/// Errors are structured to explain both what failed and why, enabling
9/// callers to take appropriate recovery actions.
10#[derive(Debug, Error)]
11pub enum NumaError {
12    /// The requested memory policy is not supported on this kernel.
13    #[error("memory policy not supported: {reason}")]
14    PolicyNotSupported {
15        /// Description of the policy that failed.
16        policy: String,
17        /// Why the policy is not supported.
18        reason: String,
19    },
20
21    /// A required capability is missing.
22    #[error("missing capability: {capability}")]
23    CapabilityMissing {
24        /// The name of the missing capability (e.g., "CAP_SYS_NICE").
25        capability: &'static str,
26    },
27
28    /// Hard mode was requested but cannot be enforced.
29    #[error("hard mode unavailable for {feature}: {reason}")]
30    HardModeUnavailable {
31        /// The feature that cannot be enforced.
32        feature: &'static str,
33        /// Why hard mode is unavailable.
34        reason: String,
35    },
36
37    /// Topology discovery failed.
38    #[error("topology discovery failed: {source}")]
39    TopologyError {
40        /// The underlying I/O error.
41        #[source]
42        source: io::Error,
43    },
44
45    /// Memory allocation or mapping failed.
46    #[error("allocation of {size} bytes failed: {source}")]
47    AllocationFailed {
48        /// The requested allocation size.
49        size: usize,
50        /// The underlying I/O error.
51        #[source]
52        source: io::Error,
53    },
54
55    /// Thread pinning failed.
56    #[error("thread pinning failed: {source}")]
57    PinningFailed {
58        /// The underlying I/O error.
59        #[source]
60        source: io::Error,
61    },
62
63    /// Memory binding (mbind) failed.
64    #[error("memory binding failed: {source}")]
65    BindFailed {
66        /// The underlying I/O error.
67        #[source]
68        source: io::Error,
69    },
70
71    /// An invalid argument was provided.
72    #[error("invalid argument: {message}")]
73    InvalidArgument {
74        /// Description of the invalid argument.
75        message: String,
76    },
77
78    /// Platform does not support the requested feature.
79    #[error("feature not supported on this platform: {feature}")]
80    NotSupported {
81        /// The unsupported feature.
82        feature: &'static str,
83    },
84
85    /// Generic I/O error.
86    #[error("I/O error: {0}")]
87    Io(#[from] io::Error),
88}
89
90impl NumaError {
91    /// Create a topology error from an I/O error.
92    pub fn topology(source: io::Error) -> Self {
93        Self::TopologyError { source }
94    }
95
96    /// Create an allocation error.
97    pub fn allocation(size: usize, source: io::Error) -> Self {
98        Self::AllocationFailed { size, source }
99    }
100
101    /// Create a pinning error from an I/O error.
102    pub fn pinning(source: io::Error) -> Self {
103        Self::PinningFailed { source }
104    }
105
106    /// Create a bind error from an I/O error.
107    pub fn bind(source: io::Error) -> Self {
108        Self::BindFailed { source }
109    }
110
111    /// Create a policy not supported error.
112    pub fn policy_not_supported(policy: impl Into<String>, reason: impl Into<String>) -> Self {
113        Self::PolicyNotSupported {
114            policy: policy.into(),
115            reason: reason.into(),
116        }
117    }
118
119    /// Create a hard mode unavailable error.
120    pub fn hard_mode_unavailable(feature: &'static str, reason: impl Into<String>) -> Self {
121        Self::HardModeUnavailable {
122            feature,
123            reason: reason.into(),
124        }
125    }
126
127    /// Create an invalid argument error.
128    pub fn invalid_argument(message: impl Into<String>) -> Self {
129        Self::InvalidArgument {
130            message: message.into(),
131        }
132    }
133
134    /// Check if this error indicates a permission issue.
135    pub fn is_permission_error(&self) -> bool {
136        match self {
137            Self::CapabilityMissing { .. } => true,
138            Self::TopologyError { source }
139            | Self::AllocationFailed { source, .. }
140            | Self::PinningFailed { source }
141            | Self::BindFailed { source } => source.kind() == io::ErrorKind::PermissionDenied,
142            Self::Io(e) => e.kind() == io::ErrorKind::PermissionDenied,
143            _ => false,
144        }
145    }
146
147    /// Check if this error indicates the feature is not supported.
148    pub fn is_not_supported(&self) -> bool {
149        matches!(
150            self,
151            Self::NotSupported { .. } | Self::PolicyNotSupported { .. }
152        )
153    }
154}
155
156/// Result type alias for numaperf operations.
157pub type Result<T> = std::result::Result<T, NumaError>;
158
159#[cfg(test)]
160mod tests {
161    use super::*;
162
163    #[test]
164    fn test_error_display() {
165        let err = NumaError::policy_not_supported("Bind", "kernel too old");
166        assert_eq!(
167            format!("{}", err),
168            "memory policy not supported: kernel too old"
169        );
170
171        let err = NumaError::CapabilityMissing {
172            capability: "CAP_SYS_NICE",
173        };
174        assert_eq!(format!("{}", err), "missing capability: CAP_SYS_NICE");
175    }
176
177    #[test]
178    fn test_error_is_permission() {
179        let err = NumaError::CapabilityMissing {
180            capability: "CAP_SYS_NICE",
181        };
182        assert!(err.is_permission_error());
183
184        // EPERM = 1 on Linux
185        let err = NumaError::pinning(io::Error::from_raw_os_error(1));
186        assert!(err.is_permission_error());
187    }
188}