j2k_cuda_runtime/
error.rs1use 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 invalid argument: {message}")]
111 InvalidArgument {
112 message: String,
114 },
115 #[error("CUDA internal invariant failed: {what}")]
117 InternalInvariant {
118 what: &'static str,
120 },
121}
122
123impl CudaError {
124 pub fn is_unavailable(&self) -> bool {
126 match self {
127 Self::Unavailable { .. } => true,
128 Self::CompletionFailed {
129 primary,
130 completion,
131 } => primary.is_unavailable() && completion.is_unavailable(),
132 _ => false,
133 }
134 }
135}
136
137pub(crate) fn select_uncertain_completion_error(
138 primary_error: CudaError,
139 completion_error: Option<CudaError>,
140) -> CudaError {
141 if let Some(completion_error) = completion_error {
142 return CudaError::CompletionFailed {
143 primary: Box::new(primary_error),
144 completion: Box::new(completion_error),
145 };
146 }
147 if matches!(
148 &primary_error,
149 CudaError::Driver { .. }
150 | CudaError::StatePoisoned { .. }
151 | CudaError::CompletionFailed { .. }
152 | CudaError::ResourceReleaseFailed { .. }
153 ) {
154 return primary_error;
155 }
156 CudaError::StatePoisoned {
157 message: format!(
158 "CUDA context became poisoned while the preceding operation failed: {primary_error}"
159 ),
160 }
161}
162
163pub(crate) fn select_resource_release_error(
164 primary_error: CudaError,
165 release_error: CudaError,
166) -> CudaError {
167 CudaError::ResourceReleaseFailed {
168 primary: Box::new(primary_error),
169 release: Box::new(release_error),
170 }
171}
172
173#[cfg(test)]
174mod tests {
175 use super::{select_resource_release_error, CudaError};
176
177 fn unavailable(message: &str) -> CudaError {
178 CudaError::Unavailable {
179 message: message.to_string(),
180 }
181 }
182
183 fn driver(operation: &'static str) -> CudaError {
184 CudaError::Driver {
185 operation,
186 code: 1,
187 name: "CUDA_ERROR_TEST".to_string(),
188 }
189 }
190
191 #[test]
192 fn mixed_completion_failure_is_not_fallback_eligible_unavailability() {
193 let mixed = CudaError::CompletionFailed {
194 primary: Box::new(unavailable("driver missing")),
195 completion: Box::new(driver("cuCtxSynchronize")),
196 };
197 assert!(!mixed.is_unavailable());
198
199 let unavailable_only = CudaError::CompletionFailed {
200 primary: Box::new(unavailable("driver missing")),
201 completion: Box::new(unavailable("completion unavailable")),
202 };
203 assert!(unavailable_only.is_unavailable());
204 }
205
206 #[test]
207 fn resource_release_failure_preserves_both_diagnostics_and_blocks_fallback() {
208 let error = select_resource_release_error(
209 unavailable("primary unavailable"),
210 driver("release pool hold"),
211 );
212 assert!(!error.is_unavailable());
213 assert!(matches!(error, CudaError::ResourceReleaseFailed { .. }));
214 let rendered = error.to_string();
215 assert!(rendered.contains("primary unavailable"));
216 assert!(rendered.contains("release pool hold"));
217 }
218}