use crate::driver::CuResult;
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum CudaError {
#[error("CUDA driver is unavailable: {message}")]
Unavailable {
message: String,
},
#[error("CUDA driver call {operation} failed with CUresult {code}{name}")]
Driver {
operation: &'static str,
code: CuResult,
name: String,
},
#[error("CUDA copy output buffer too small: required {required}, have {have}")]
OutputTooSmall {
required: usize,
have: usize,
},
#[error("CUDA byte length is too large for kernel launch: {len}")]
LengthTooLarge {
len: usize,
},
#[error("CUDA host allocation failed for {bytes} bytes")]
HostAllocationFailed {
bytes: usize,
},
#[error(
"CUDA host allocation for {what} is too large: requested {requested} bytes, cap {cap} bytes"
)]
HostAllocationTooLarge {
requested: usize,
cap: usize,
what: &'static str,
},
#[error("CUDA buffer length {bytes} is not a multiple of typed element size {element_size}")]
LengthNotElementAligned {
bytes: usize,
element_size: usize,
},
#[error("CUDA image allocation size overflow for {width}x{height}x{channels}")]
ImageTooLarge {
width: u32,
height: u32,
channels: usize,
},
#[error("CUDA runtime state lock is poisoned: {message}")]
StatePoisoned {
message: String,
},
#[error(
"CUDA operation failed ({primary}); context completion could not be established ({completion})"
)]
CompletionFailed {
primary: Box<CudaError>,
completion: Box<CudaError>,
},
#[error(
"CUDA operation failed ({primary}); retained resource release also failed ({release})"
)]
ResourceReleaseFailed {
primary: Box<CudaError>,
release: Box<CudaError>,
},
#[error("CUDA kernel {kernel} reported status {code} detail {detail}")]
KernelStatus {
kernel: &'static str,
code: u32,
detail: u32,
},
#[error("CUDA invalid argument: {message}")]
InvalidArgument {
message: String,
},
#[error("CUDA internal invariant failed: {what}")]
InternalInvariant {
what: &'static str,
},
}
impl CudaError {
pub fn is_unavailable(&self) -> bool {
match self {
Self::Unavailable { .. } => true,
Self::CompletionFailed {
primary,
completion,
} => primary.is_unavailable() && completion.is_unavailable(),
_ => false,
}
}
}
pub(crate) fn select_uncertain_completion_error(
primary_error: CudaError,
completion_error: Option<CudaError>,
) -> CudaError {
if let Some(completion_error) = completion_error {
return CudaError::CompletionFailed {
primary: Box::new(primary_error),
completion: Box::new(completion_error),
};
}
if matches!(
&primary_error,
CudaError::Driver { .. }
| CudaError::StatePoisoned { .. }
| CudaError::CompletionFailed { .. }
| CudaError::ResourceReleaseFailed { .. }
) {
return primary_error;
}
CudaError::StatePoisoned {
message: format!(
"CUDA context became poisoned while the preceding operation failed: {primary_error}"
),
}
}
pub(crate) fn select_resource_release_error(
primary_error: CudaError,
release_error: CudaError,
) -> CudaError {
CudaError::ResourceReleaseFailed {
primary: Box::new(primary_error),
release: Box::new(release_error),
}
}
#[cfg(test)]
mod tests {
use super::{select_resource_release_error, CudaError};
fn unavailable(message: &str) -> CudaError {
CudaError::Unavailable {
message: message.to_string(),
}
}
fn driver(operation: &'static str) -> CudaError {
CudaError::Driver {
operation,
code: 1,
name: "CUDA_ERROR_TEST".to_string(),
}
}
#[test]
fn mixed_completion_failure_is_not_fallback_eligible_unavailability() {
let mixed = CudaError::CompletionFailed {
primary: Box::new(unavailable("driver missing")),
completion: Box::new(driver("cuCtxSynchronize")),
};
assert!(!mixed.is_unavailable());
let unavailable_only = CudaError::CompletionFailed {
primary: Box::new(unavailable("driver missing")),
completion: Box::new(unavailable("completion unavailable")),
};
assert!(unavailable_only.is_unavailable());
}
#[test]
fn resource_release_failure_preserves_both_diagnostics_and_blocks_fallback() {
let error = select_resource_release_error(
unavailable("primary unavailable"),
driver("release pool hold"),
);
assert!(!error.is_unavailable());
assert!(matches!(error, CudaError::ResourceReleaseFailed { .. }));
let rendered = error.to_string();
assert!(rendered.contains("primary unavailable"));
assert!(rendered.contains("release pool hold"));
}
}