Skip to main content

singe_cusparse/
error.rs

1use std::{
2    ffi::{CStr, NulError},
3    fmt, result,
4};
5
6use singe_cuda::error::Error as CudaError;
7use singe_cusparse_sys as sys;
8use thiserror::Error;
9
10/// [`Status`] is the cuSPARSE status code returned by cuSPARSE operations.
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
12#[non_exhaustive]
13pub enum Status {
14    /// The operation completed successfully.
15    Success,
16    /// The cuSPARSE library was not initialized.
17    /// Common causes include a missing [`Context::create`](crate::context::Context::create) call, a CUDA runtime error reported by cuSPARSE, or an error in the hardware setup.
18    /// The error also applies when a matrix or vector descriptor is not initialized.
19    NotInitialized,
20    /// Resource allocation failed inside the cuSPARSE library.
21    /// Usually caused by a device memory allocation (`cudaMalloc()`) or host memory allocation failure.
22    AllocFailed,
23    /// An unsupported value or parameter was passed to the operation, such as a negative vector size.
24    InvalidValue,
25    /// The operation requires a feature absent from the device architecture.
26    ArchitectureMismatch,
27    /// An access to GPU memory space failed, which is usually caused by a
28    /// failure to bind a texture or an invalid device pointer.
29    /// To correct: check that all device pointers are valid and that no texture
30    /// is bound that could interfere with the operation.
31    MappingError,
32    /// The GPU program failed to execute.
33    /// A kernel launch failure on the GPU is a common cause.
34    ExecutionFailed,
35    /// An internal cuSPARSE operation failed.
36    /// Also check that memory passed to the operation is not released before the operation completes.
37    InternalError,
38    /// The matrix type is not supported by this operation.
39    /// Usually caused by passing an invalid matrix descriptor to the operation.
40    /// Check that the fields in [`MatrixDescriptor`](crate::matrix::MatrixDescriptor) were set correctly.
41    MatrixTypeNotSupported,
42    /// A zero pivot was encountered during the factorization.
43    /// The operation cannot proceed because a diagonal element is zero.
44    /// To correct: ensure the input matrix is non-singular.
45    ZeroPivot,
46    /// The operation or data type combination is currently not supported.
47    NotSupported,
48    /// The resources for the computation, such as GPU global or shared memory, are not sufficient to complete the operation.
49    /// The error can also indicate that the current computation mode, such as sparse matrix index bit size, cannot handle the given input.
50    InsufficientResources,
51    /// A status code not known to this version of Singe.
52    Unknown(u32),
53}
54
55impl Status {
56    pub fn description(self) -> String {
57        match sys::cusparseStatus_t::try_from(self.raw()) {
58            Ok(status) => cusparse_error_string(status),
59            Err(_) => String::from("unknown cusparse error"),
60        }
61    }
62
63    pub const fn raw(self) -> u32 {
64        match self {
65            Self::Success => sys::cusparseStatus_t::CUSPARSE_STATUS_SUCCESS as _,
66            Self::NotInitialized => sys::cusparseStatus_t::CUSPARSE_STATUS_NOT_INITIALIZED as _,
67            Self::AllocFailed => sys::cusparseStatus_t::CUSPARSE_STATUS_ALLOC_FAILED as _,
68            Self::InvalidValue => sys::cusparseStatus_t::CUSPARSE_STATUS_INVALID_VALUE as _,
69            Self::ArchitectureMismatch => sys::cusparseStatus_t::CUSPARSE_STATUS_ARCH_MISMATCH as _,
70            Self::MappingError => sys::cusparseStatus_t::CUSPARSE_STATUS_MAPPING_ERROR as _,
71            Self::ExecutionFailed => sys::cusparseStatus_t::CUSPARSE_STATUS_EXECUTION_FAILED as _,
72            Self::InternalError => sys::cusparseStatus_t::CUSPARSE_STATUS_INTERNAL_ERROR as _,
73            Self::MatrixTypeNotSupported => {
74                sys::cusparseStatus_t::CUSPARSE_STATUS_MATRIX_TYPE_NOT_SUPPORTED as _
75            }
76            Self::ZeroPivot => sys::cusparseStatus_t::CUSPARSE_STATUS_ZERO_PIVOT as _,
77            Self::NotSupported => sys::cusparseStatus_t::CUSPARSE_STATUS_NOT_SUPPORTED as _,
78            Self::InsufficientResources => {
79                sys::cusparseStatus_t::CUSPARSE_STATUS_INSUFFICIENT_RESOURCES as _
80            }
81            Self::Unknown(code) => code,
82        }
83    }
84}
85
86impl TryFrom<u32> for Status {
87    type Error = u32;
88
89    fn try_from(code: u32) -> result::Result<Self, u32> {
90        match code {
91            code if code == sys::cusparseStatus_t::CUSPARSE_STATUS_SUCCESS as u32 => {
92                Ok(Self::Success)
93            }
94            code if code == sys::cusparseStatus_t::CUSPARSE_STATUS_NOT_INITIALIZED as u32 => {
95                Ok(Self::NotInitialized)
96            }
97            code if code == sys::cusparseStatus_t::CUSPARSE_STATUS_ALLOC_FAILED as u32 => {
98                Ok(Self::AllocFailed)
99            }
100            code if code == sys::cusparseStatus_t::CUSPARSE_STATUS_INVALID_VALUE as u32 => {
101                Ok(Self::InvalidValue)
102            }
103            code if code == sys::cusparseStatus_t::CUSPARSE_STATUS_ARCH_MISMATCH as u32 => {
104                Ok(Self::ArchitectureMismatch)
105            }
106            code if code == sys::cusparseStatus_t::CUSPARSE_STATUS_MAPPING_ERROR as u32 => {
107                Ok(Self::MappingError)
108            }
109            code if code == sys::cusparseStatus_t::CUSPARSE_STATUS_EXECUTION_FAILED as u32 => {
110                Ok(Self::ExecutionFailed)
111            }
112            code if code == sys::cusparseStatus_t::CUSPARSE_STATUS_INTERNAL_ERROR as u32 => {
113                Ok(Self::InternalError)
114            }
115            code if code
116                == sys::cusparseStatus_t::CUSPARSE_STATUS_MATRIX_TYPE_NOT_SUPPORTED as u32 =>
117            {
118                Ok(Self::MatrixTypeNotSupported)
119            }
120            code if code == sys::cusparseStatus_t::CUSPARSE_STATUS_ZERO_PIVOT as u32 => {
121                Ok(Self::ZeroPivot)
122            }
123            code if code == sys::cusparseStatus_t::CUSPARSE_STATUS_NOT_SUPPORTED as u32 => {
124                Ok(Self::NotSupported)
125            }
126            code if code
127                == sys::cusparseStatus_t::CUSPARSE_STATUS_INSUFFICIENT_RESOURCES as u32 =>
128            {
129                Ok(Self::InsufficientResources)
130            }
131            code => Err(code),
132        }
133    }
134}
135
136impl From<sys::cusparseStatus_t> for Status {
137    fn from(status: sys::cusparseStatus_t) -> Self {
138        Self::try_from(status as u32).unwrap_or_else(Self::Unknown)
139    }
140}
141
142impl TryFrom<Status> for sys::cusparseStatus_t {
143    type Error = Status;
144
145    fn try_from(status: Status) -> result::Result<Self, Status> {
146        match status {
147            Status::Success => Ok(Self::CUSPARSE_STATUS_SUCCESS),
148            Status::NotInitialized => Ok(Self::CUSPARSE_STATUS_NOT_INITIALIZED),
149            Status::AllocFailed => Ok(Self::CUSPARSE_STATUS_ALLOC_FAILED),
150            Status::InvalidValue => Ok(Self::CUSPARSE_STATUS_INVALID_VALUE),
151            Status::ArchitectureMismatch => Ok(Self::CUSPARSE_STATUS_ARCH_MISMATCH),
152            Status::MappingError => Ok(Self::CUSPARSE_STATUS_MAPPING_ERROR),
153            Status::ExecutionFailed => Ok(Self::CUSPARSE_STATUS_EXECUTION_FAILED),
154            Status::InternalError => Ok(Self::CUSPARSE_STATUS_INTERNAL_ERROR),
155            Status::MatrixTypeNotSupported => Ok(Self::CUSPARSE_STATUS_MATRIX_TYPE_NOT_SUPPORTED),
156            Status::ZeroPivot => Ok(Self::CUSPARSE_STATUS_ZERO_PIVOT),
157            Status::NotSupported => Ok(Self::CUSPARSE_STATUS_NOT_SUPPORTED),
158            Status::InsufficientResources => Ok(Self::CUSPARSE_STATUS_INSUFFICIENT_RESOURCES),
159            Status::Unknown(_) => Err(status),
160        }
161    }
162}
163
164impl fmt::Display for Status {
165    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
166        match self {
167            Self::Success => f.write_str("CUSPARSE_STATUS_SUCCESS"),
168            Self::NotInitialized => f.write_str("CUSPARSE_STATUS_NOT_INITIALIZED"),
169            Self::AllocFailed => f.write_str("CUSPARSE_STATUS_ALLOC_FAILED"),
170            Self::InvalidValue => f.write_str("CUSPARSE_STATUS_INVALID_VALUE"),
171            Self::ArchitectureMismatch => f.write_str("CUSPARSE_STATUS_ARCH_MISMATCH"),
172            Self::MappingError => f.write_str("CUSPARSE_STATUS_MAPPING_ERROR"),
173            Self::ExecutionFailed => f.write_str("CUSPARSE_STATUS_EXECUTION_FAILED"),
174            Self::InternalError => f.write_str("CUSPARSE_STATUS_INTERNAL_ERROR"),
175            Self::MatrixTypeNotSupported => {
176                f.write_str("CUSPARSE_STATUS_MATRIX_TYPE_NOT_SUPPORTED")
177            }
178            Self::ZeroPivot => f.write_str("CUSPARSE_STATUS_ZERO_PIVOT"),
179            Self::NotSupported => f.write_str("CUSPARSE_STATUS_NOT_SUPPORTED"),
180            Self::InsufficientResources => f.write_str("CUSPARSE_STATUS_INSUFFICIENT_RESOURCES"),
181            Self::Unknown(code) => write!(f, "UNKNOWN_CUSPARSE_STATUS({code})"),
182        }
183    }
184}
185
186fn cusparse_error_string(status: sys::cusparseStatus_t) -> String {
187    unsafe {
188        let c_ptr = sys::cusparseGetErrorString(status);
189        if c_ptr.is_null() {
190            String::from("unknown cusparse error")
191        } else {
192            CStr::from_ptr(c_ptr).to_string_lossy().into_owned()
193        }
194    }
195}
196
197#[derive(Error, Debug)]
198#[non_exhaustive]
199pub enum Error {
200    #[error("cuda error: {0}")]
201    Cuda(#[from] CudaError),
202
203    #[error("cusparse error ({code}): {message}")]
204    Cusparse { code: Status, message: String },
205
206    #[error("string contains interior nul byte")]
207    InteriorNul,
208
209    #[error("unexpected null handle")]
210    NullHandle,
211
212    #[error("`{name}` is out of range")]
213    OutOfRange { name: String },
214
215    #[error("scalar pointer modes do not match")]
216    ScalarPointerModeMismatch,
217
218    #[error("plan belongs to a different cusparse context")]
219    PlanContextMismatch,
220
221    #[error("stream belongs to a different cuda context")]
222    StreamContextMismatch,
223}
224
225pub type Result<T> = result::Result<T, Error>;
226
227impl From<sys::cusparseStatus_t> for Error {
228    fn from(status: sys::cusparseStatus_t) -> Self {
229        let code = Status::from(status);
230        Self::from(code)
231    }
232}
233
234impl From<Status> for Error {
235    fn from(code: Status) -> Self {
236        Self::Cusparse {
237            code,
238            message: code.description(),
239        }
240    }
241}
242
243impl From<NulError> for Error {
244    fn from(_: NulError) -> Self {
245        Self::InteriorNul
246    }
247}
248
249#[macro_export]
250macro_rules! try_ffi {
251    ($expr:expr) => {{
252        let status = { $expr };
253        if status != singe_cusparse_sys::cusparseStatus_t::CUSPARSE_STATUS_SUCCESS {
254            Err($crate::error::Error::from(status))
255        } else {
256            Ok(())
257        }
258    }};
259}