error.hpp
Go to the documentation of this file.
1 /*
2  * SPDX-FileCopyrightText: Copyright (c) 2019-2026, NVIDIA CORPORATION.
3  * SPDX-License-Identifier: Apache-2.0
4  */
5 
6 #pragma once
7 
8 #include <cudf/utilities/export.hpp>
9 
10 #include <cuda.h>
11 #include <cuda_runtime_api.h>
12 
13 #include <stdexcept>
14 #include <string>
15 #include <type_traits>
16 
17 namespace CUDF_EXPORT cudf {
30 struct logic_error : std::logic_error {
36  explicit logic_error(char const* const message) : std::logic_error(message) {}
37 
43  explicit logic_error(std::string const& message) : std::logic_error(message) {}
44 
45  // TODO Add an error code member? This would be useful for translating an
46  // exception to an error code in a pure-C API
47 
48  ~logic_error() override
49  {
50  // Needed so that the first instance of the implicit destructor for any TU isn't 'constructed'
51  // from a host+device function marking the implicit version also as host+device
52  }
53 };
58 struct cuda_error : std::runtime_error {
65  explicit cuda_error(std::string const& message, cudaError_t const& error)
66  : std::runtime_error(message), _cudaError(error)
67  {
68  }
69 
75  [[nodiscard]] cudaError_t error_code() const { return _cudaError; }
76 
77  protected:
78  cudaError_t _cudaError;
79 };
80 
82  using cuda_error::cuda_error; // Inherit constructors
83 };
84 
92 struct data_type_error : std::invalid_argument {
98  explicit data_type_error(char const* const message) : std::invalid_argument(message) {}
99 
105  explicit data_type_error(std::string const& message) : std::invalid_argument(message) {}
106 };
109 } // namespace CUDF_EXPORT cudf
110 
111 #define STRINGIFY_DETAIL(x) #x
112 #define CUDF_STRINGIFY(x) STRINGIFY_DETAIL(x)
113 
145 #define CUDF_EXPECTS(...) \
146  GET_CUDF_EXPECTS_MACRO(__VA_ARGS__, CUDF_EXPECTS_3, CUDF_EXPECTS_2) \
147  (__VA_ARGS__)
148 
150 
151 #define GET_CUDF_EXPECTS_MACRO(_1, _2, _3, NAME, ...) NAME
152 
153 #define CUDF_EXPECTS_3(_condition, _reason, _exception_type) \
154  do { \
155  static_assert(std::is_base_of_v<std::exception, _exception_type>); \
156  if (!(_condition)) { \
157  cudf::detail::cudf_fail<_exception_type>( \
158  [&]() -> std::string { return _reason; }, __LINE__, __FILE__); \
159  } \
160  } while (0)
161 
162 #define CUDF_EXPECTS_2(_condition, _reason) CUDF_EXPECTS_3(_condition, _reason, cudf::logic_error)
163 
165 
186 #define CUDF_FAIL(...) \
187  GET_CUDF_FAIL_MACRO(__VA_ARGS__, CUDF_FAIL_2, CUDF_FAIL_1) \
188  (__VA_ARGS__)
189 
191 
192 #define GET_CUDF_FAIL_MACRO(_1, _2, NAME, ...) NAME
193 
194 #define CUDF_FAIL_2(_what, _exception_type) \
195  cudf::detail::cudf_fail<_exception_type>( \
196  [&]() -> std::string { return _what; }, __LINE__, __FILE__)
197 
198 #define CUDF_FAIL_1(_what) CUDF_FAIL_2(_what, cudf::logic_error)
199 
201 
202 namespace CUDF_EXPORT cudf {
203 namespace detail {
204 // @cond
205 inline void throw_cuda_error(cudaError_t error, char const* file, unsigned int line)
206 {
207  // Calls cudaGetLastError to clear the error status. It is nearly certain that a fatal error
208  // occurred if it still returns the same error after a cleanup.
209  cudaGetLastError();
210  auto const last = cudaFree(nullptr);
211  auto const msg = std::string{"CUDA error encountered at: " + std::string{file} + ":" +
212  std::to_string(line) + ": " + std::to_string(error) + " " +
213  cudaGetErrorName(error) + " " + cudaGetErrorString(error)};
214  // Call cudaDeviceSynchronize to ensure `last` did not result from an asynchronous error.
215  // between two calls.
216  if (error == last && last == cudaDeviceSynchronize()) {
217  throw fatal_cuda_error{"Fatal " + msg, error};
218  } else {
219  throw cuda_error{msg, error};
220  }
221 }
222 // @endcond
223 
224 // @cond
225 template <typename Exception, typename MsgFunc>
226 [[noreturn]] void cudf_fail(MsgFunc&& msg_func, int line_number, char const* filename)
227 {
228  std::string const msg = std::forward<MsgFunc>(msg_func)();
229  throw Exception{std::string{"CUDF failure at: "} + filename + ":" + std::to_string(line_number) +
230  ": " + (msg.empty() ? "(no message)" : msg)};
231 }
232 // @endcond
233 } // namespace detail
234 } // namespace CUDF_EXPORT cudf
235 
243 #define CUDF_CUDA_TRY(call) \
244  do { \
245  cudaError_t const status = (call); \
246  if (cudaSuccess != status) { cudf::detail::throw_cuda_error(status, __FILE__, __LINE__); } \
247  } while (0);
248 
262 #ifndef NDEBUG
263 #define CUDF_CHECK_CUDA(stream) \
264  do { \
265  CUDF_CUDA_TRY(cudaStreamSynchronize(stream)); \
266  CUDF_CUDA_TRY(cudaPeekAtLastError()); \
267  } while (0);
268 #else
269 #define CUDF_CHECK_CUDA(stream) CUDF_CUDA_TRY(cudaPeekAtLastError());
270 #endif
cuDF interfaces
Definition: host_udf.hpp:26
Exception thrown when a CUDA error is encountered.
Definition: error.hpp:58
cuda_error(std::string const &message, cudaError_t const &error)
Construct a new cuda error object with error message and code.
Definition: error.hpp:65
cudaError_t _cudaError
CUDA error code.
Definition: error.hpp:78
cudaError_t error_code() const
Returns the CUDA error code associated with the exception.
Definition: error.hpp:75
Exception thrown when an operation is attempted on an unsupported dtype.
Definition: error.hpp:92
data_type_error(std::string const &message)
Construct a new data_type_error object with error message.
Definition: error.hpp:105
data_type_error(char const *const message)
Constructs a data_type_error with the error message.
Definition: error.hpp:98
Exception thrown when logical precondition is violated.
Definition: error.hpp:30
logic_error(char const *const message)
Constructs a logic_error with the error message.
Definition: error.hpp:36
logic_error(std::string const &message)
Construct a new logic error object with error message.
Definition: error.hpp:43