1use crate::driver::CuResult;
2
3#[derive(Debug, thiserror::Error)]
5#[non_exhaustive]
6pub enum CudaError {
7 #[error("CUDA driver is unavailable: {message}")]
9 Unavailable {
10 message: String,
12 },
13 #[error("CUDA driver call {operation} failed with CUresult {code}{name}")]
15 Driver {
16 operation: &'static str,
18 code: CuResult,
20 name: String,
22 },
23 #[error("CUDA copy output buffer too small: required {required}, have {have}")]
25 OutputTooSmall {
26 required: usize,
28 have: usize,
30 },
31 #[error("CUDA byte length is too large for kernel launch: {len}")]
33 LengthTooLarge {
34 len: usize,
36 },
37 #[error("CUDA host allocation failed for {bytes} bytes")]
39 HostAllocationFailed {
40 bytes: usize,
42 },
43 #[error(
45 "CUDA host allocation for {what} is too large: requested {requested} bytes, cap {cap} bytes"
46 )]
47 HostAllocationTooLarge {
48 requested: usize,
50 cap: usize,
52 what: &'static str,
54 },
55 #[error("CUDA buffer length {bytes} is not a multiple of typed element size {element_size}")]
57 LengthNotElementAligned {
58 bytes: usize,
60 element_size: usize,
62 },
63 #[error("CUDA image allocation size overflow for {width}x{height}x{channels}")]
65 ImageTooLarge {
66 width: u32,
68 height: u32,
70 channels: usize,
72 },
73 #[error("CUDA runtime state lock is poisoned: {message}")]
75 StatePoisoned {
76 message: String,
78 },
79 #[error(
81 "CUDA operation failed ({primary}); context completion could not be established ({completion})"
82 )]
83 CompletionFailed {
84 primary: Box<CudaError>,
86 completion: Box<CudaError>,
88 },
89 #[error(
91 "CUDA operation failed ({primary}); retained resource release also failed ({release})"
92 )]
93 ResourceReleaseFailed {
94 primary: Box<CudaError>,
96 release: Box<CudaError>,
98 },
99 #[error("CUDA kernel {kernel} reported status {code} detail {detail}")]
101 KernelStatus {
102 kernel: &'static str,
104 code: u32,
106 detail: u32,
108 },
109 #[error("CUDA kernel {kernel} reported status {code} detail {detail} for job {job_index}")]
111 KernelJobStatus {
112 kernel: &'static str,
114 job_index: usize,
116 code: u32,
118 detail: u32,
120 },
121 #[error("CUDA invalid argument: {message}")]
123 InvalidArgument {
124 message: String,
126 },
127 #[error("CUDA internal invariant failed: {what}")]
129 InternalInvariant {
130 what: &'static str,
132 },
133}
134
135impl CudaError {
136 pub fn is_unavailable(&self) -> bool {
138 match self {
139 Self::Unavailable { .. } => true,
140 Self::CompletionFailed {
141 primary,
142 completion,
143 } => primary.is_unavailable() && completion.is_unavailable(),
144 _ => false,
145 }
146 }
147
148 #[doc(hidden)]
156 pub fn completion_is_uncertain(&self) -> bool {
157 matches!(
158 self,
159 Self::Driver { .. }
160 | Self::StatePoisoned { .. }
161 | Self::CompletionFailed { .. }
162 | Self::ResourceReleaseFailed { .. }
163 )
164 }
165
166 #[doc(hidden)]
168 pub fn kernel_job_index(&self) -> Option<usize> {
169 match self {
170 Self::KernelJobStatus { job_index, .. } => Some(*job_index),
171 Self::CompletionFailed { primary, .. }
172 | Self::ResourceReleaseFailed {
173 primary,
174 release: _,
175 } => primary.kernel_job_index(),
176 _ => None,
177 }
178 }
179
180 #[doc(hidden)]
187 pub fn session_is_unusable(&self) -> bool {
188 matches!(
189 self,
190 Self::Unavailable { .. }
191 | Self::Driver { .. }
192 | Self::StatePoisoned { .. }
193 | Self::CompletionFailed { .. }
194 | Self::ResourceReleaseFailed { .. }
195 )
196 }
197}
198
199pub(crate) fn select_uncertain_completion_error(
200 primary_error: CudaError,
201 completion_error: Option<CudaError>,
202) -> CudaError {
203 if let Some(completion_error) = completion_error {
204 return CudaError::CompletionFailed {
205 primary: Box::new(primary_error),
206 completion: Box::new(completion_error),
207 };
208 }
209 if matches!(
210 &primary_error,
211 CudaError::Driver { .. }
212 | CudaError::StatePoisoned { .. }
213 | CudaError::CompletionFailed { .. }
214 | CudaError::ResourceReleaseFailed { .. }
215 ) {
216 return primary_error;
217 }
218 CudaError::StatePoisoned {
219 message: format!(
220 "CUDA context became poisoned while the preceding operation failed: {primary_error}"
221 ),
222 }
223}
224
225pub(crate) fn select_resource_release_error(
226 primary_error: CudaError,
227 release_error: CudaError,
228) -> CudaError {
229 CudaError::ResourceReleaseFailed {
230 primary: Box::new(primary_error),
231 release: Box::new(release_error),
232 }
233}
234
235#[cfg(test)]
236mod tests {
237 use super::{select_resource_release_error, CudaError};
238
239 fn unavailable(message: &str) -> CudaError {
240 CudaError::Unavailable {
241 message: message.to_string(),
242 }
243 }
244
245 fn driver(operation: &'static str) -> CudaError {
246 CudaError::Driver {
247 operation,
248 code: 1,
249 name: "CUDA_ERROR_TEST".to_string(),
250 }
251 }
252
253 #[test]
254 fn mixed_completion_failure_is_not_fallback_eligible_unavailability() {
255 let mixed = CudaError::CompletionFailed {
256 primary: Box::new(unavailable("driver missing")),
257 completion: Box::new(driver("cuCtxSynchronize")),
258 };
259 assert!(!mixed.is_unavailable());
260
261 let unavailable_only = CudaError::CompletionFailed {
262 primary: Box::new(unavailable("driver missing")),
263 completion: Box::new(unavailable("completion unavailable")),
264 };
265 assert!(unavailable_only.is_unavailable());
266 }
267
268 #[test]
269 fn resource_release_failure_preserves_both_diagnostics_and_blocks_fallback() {
270 let error = select_resource_release_error(
271 unavailable("primary unavailable"),
272 driver("release pool hold"),
273 );
274 assert!(!error.is_unavailable());
275 assert!(matches!(error, CudaError::ResourceReleaseFailed { .. }));
276 let rendered = error.to_string();
277 assert!(rendered.contains("primary unavailable"));
278 assert!(rendered.contains("release pool hold"));
279 }
280
281 #[test]
282 fn completion_uncertainty_excludes_preflight_and_validated_kernel_status() {
283 assert!(!CudaError::InvalidArgument {
284 message: "preflight".to_string(),
285 }
286 .completion_is_uncertain());
287 assert!(!CudaError::KernelStatus {
288 kernel: "test",
289 code: 1,
290 detail: 2,
291 }
292 .completion_is_uncertain());
293 assert!(driver("cuEventSynchronize").completion_is_uncertain());
294 }
295}