Skip to main content

j2k_cuda_runtime/
error.rs

1use crate::driver::CuResult;
2
3/// Error returned by CUDA driver and J2K CUDA kernel helpers.
4#[derive(Debug, thiserror::Error)]
5#[non_exhaustive]
6pub enum CudaError {
7    /// CUDA driver library or device is unavailable.
8    #[error("CUDA driver is unavailable: {message}")]
9    Unavailable {
10        /// Human-readable availability failure.
11        message: String,
12    },
13    /// CUDA Driver API call failed.
14    #[error("CUDA driver call {operation} failed with CUresult {code}{name}")]
15    Driver {
16        /// Driver operation name.
17        operation: &'static str,
18        /// Raw CUDA result code.
19        code: CuResult,
20        /// CUDA error name, when available.
21        name: String,
22    },
23    /// Host output buffer is too small for a device download.
24    #[error("CUDA copy output buffer too small: required {required}, have {have}")]
25    OutputTooSmall {
26        /// Required byte count.
27        required: usize,
28        /// Provided byte count.
29        have: usize,
30    },
31    /// Byte length cannot be represented by the kernel ABI.
32    #[error("CUDA byte length is too large for kernel launch: {len}")]
33    LengthTooLarge {
34        /// Byte length.
35        len: usize,
36    },
37    /// A host-side allocation needed by a CUDA operation could not be reserved.
38    #[error("CUDA host allocation failed for {bytes} bytes")]
39    HostAllocationFailed {
40        /// Requested host allocation size in bytes.
41        bytes: usize,
42    },
43    /// Simultaneously live host allocations would exceed the codec policy.
44    #[error(
45        "CUDA host allocation for {what} is too large: requested {requested} bytes, cap {cap} bytes"
46    )]
47    HostAllocationTooLarge {
48        /// Aggregate requested host byte count, saturated on overflow.
49        requested: usize,
50        /// Maximum permitted simultaneously live host bytes.
51        cap: usize,
52        /// Logical operation requiring the allocation.
53        what: &'static str,
54    },
55    /// Device byte length is not aligned to the requested typed view element.
56    #[error("CUDA buffer length {bytes} is not a multiple of typed element size {element_size}")]
57    LengthNotElementAligned {
58        /// Byte length.
59        bytes: usize,
60        /// Requested element size.
61        element_size: usize,
62    },
63    /// Image dimensions overflowed allocation or launch geometry.
64    #[error("CUDA image allocation size overflow for {width}x{height}x{channels}")]
65    ImageTooLarge {
66        /// Image width.
67        width: u32,
68        /// Image height.
69        height: u32,
70        /// Channel count.
71        channels: usize,
72    },
73    /// Internal runtime state lock was poisoned.
74    #[error("CUDA runtime state lock is poisoned: {message}")]
75    StatePoisoned {
76        /// Poison error message.
77        message: String,
78    },
79    /// A CUDA operation failed and the follow-up completion check also failed.
80    #[error(
81        "CUDA operation failed ({primary}); context completion could not be established ({completion})"
82    )]
83    CompletionFailed {
84        /// Error returned by the original CUDA operation.
85        primary: Box<CudaError>,
86        /// Error returned while establishing context-wide completion.
87        completion: Box<CudaError>,
88    },
89    /// A CUDA operation failed and releasing its retained resources also failed.
90    #[error(
91        "CUDA operation failed ({primary}); retained resource release also failed ({release})"
92    )]
93    ResourceReleaseFailed {
94        /// Error returned by the original CUDA operation.
95        primary: Box<CudaError>,
96        /// Error returned while releasing retained resources.
97        release: Box<CudaError>,
98    },
99    /// A J2K CUDA kernel reported a validated runtime failure.
100    #[error("CUDA kernel {kernel} reported status {code} detail {detail}")]
101    KernelStatus {
102        /// Kernel entry point or logical stage name.
103        kernel: &'static str,
104        /// Kernel-defined status code.
105        code: u32,
106        /// Kernel-defined detail code.
107        detail: u32,
108    },
109    /// Caller supplied arguments that cannot be represented by this runtime API.
110    #[error("CUDA invalid argument: {message}")]
111    InvalidArgument {
112        /// Human-readable validation failure.
113        message: String,
114    },
115    /// Validated host-side planning state contradicted materialized launch data.
116    #[error("CUDA internal invariant failed: {what}")]
117    InternalInvariant {
118        /// Stable description of the failed invariant.
119        what: &'static str,
120    },
121}
122
123impl CudaError {
124    /// True when the error means the CUDA driver or device is unavailable.
125    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}