error.hpp
1 
6 #pragma once
7 
8 #include <cstdlib>
9 #include <exception>
10 #include <iostream>
11 #include <source_location>
12 #include <sstream>
13 #include <stdexcept>
14 #include <string>
15 #include <string_view>
16 
17 #include <rapidsmpf/utils/misc.hpp>
18 
19 namespace rapidsmpf {
20 
26 struct cuda_error : public std::runtime_error {
27  using std::runtime_error::runtime_error;
28 };
29 
35 class bad_alloc : public std::bad_alloc {
36  public:
42  explicit bad_alloc(char const* msg)
43  : what_{std::string{std::bad_alloc::what()} + ": " + msg} {}
44 
50  explicit bad_alloc(std::string const& msg) : bad_alloc{msg.c_str()} {}
51 
57  [[nodiscard]] char const* what() const noexcept override {
58  return what_.c_str();
59  }
60 
61  private:
62  std::string what_;
63 };
64 
72 class out_of_memory : public bad_alloc {
73  public:
79  explicit out_of_memory(char const* msg)
80  : bad_alloc{std::string{"out_of_memory: "} + msg} {}
81 
87  explicit out_of_memory(std::string const& msg) : out_of_memory{msg.c_str()} {}
88 };
89 
100 class reservation_error : public bad_alloc {
101  public:
107  explicit reservation_error(char const* msg)
108  : bad_alloc{std::string{"reservation_error: "} + msg} {}
109 
115  explicit reservation_error(std::string const& msg) : reservation_error{msg.c_str()} {}
116 };
117 
118 namespace detail {
119 
127 inline std::string build_error_message(
128  std::string_view reason,
129  std::source_location const& loc = std::source_location::current()
130 ) {
131  std::ostringstream ss;
132  ss << "RapidsMPF fatal error at: " << loc.file_name() << ":" << loc.line() << ": "
133  << reason;
134  return ss.str();
135 }
136 
145 template <typename ThrowFn>
146 constexpr void expects_impl(
147  bool condition,
148  std::string_view reason,
149  ThrowFn&& throw_fn,
150  std::source_location const& loc = std::source_location::current()
151 ) {
152  if (!condition) {
153  throw_fn(build_error_message(reason, loc));
154  }
155 }
156 
164 inline std::string build_cuda_error_message(
165  cudaError_t error, std::source_location const& loc = std::source_location::current()
166 ) {
167  std::ostringstream ss;
168  ss << "CUDA error at: " << loc.file_name() << ":" << loc.line() << ": "
169  << cudaGetErrorName(error) << " " << cudaGetErrorString(error);
170  return ss.str();
171 }
172 
182  cudaError_t error,
183  std::size_t num_bytes,
184  std::source_location const& loc = std::source_location::current()
185 ) {
186  std::ostringstream ss;
187  ss << "CUDA error (failed to allocate " << num_bytes
188  << " bytes) at: " << loc.file_name() << ":" << loc.line() << ": "
189  << cudaGetErrorName(error) << " " << cudaGetErrorString(error);
190  return ss.str();
191 }
192 
203 [[noreturn]] inline void fatal_error(
204  std::string_view reason,
205  std::source_location const& loc = std::source_location::current()
206 ) noexcept {
207  std::cerr << "RapidsMPF fatal error at: " << loc.file_name() << ":" << loc.line()
208  << ": " << reason << std::endl;
209  std::terminate();
210 }
211 
219 constexpr void expects_fatal_impl(
220  bool condition,
221  std::string_view reason,
222  std::source_location const& loc = std::source_location::current()
223 ) noexcept {
224  if (!condition) {
225  detail::fatal_error(reason, loc);
226  }
227 }
228 
235 [[noreturn]] inline void fatal_impl(
236  std::string_view reason,
237  std::source_location const& loc = std::source_location::current()
238 ) noexcept {
239  detail::fatal_error(reason, loc);
240 }
241 
242 } // namespace detail
243 
268 #define RAPIDSMPF_EXPECTS(...) \
269  GET_RAPIDSMPF_EXPECTS_MACRO(__VA_ARGS__, RAPIDSMPF_EXPECTS_3, RAPIDSMPF_EXPECTS_2) \
270  (__VA_ARGS__)
271 
272 #define GET_RAPIDSMPF_EXPECTS_MACRO(_1, _2, _3, NAME, ...) NAME
273 
274 #define RAPIDSMPF_EXPECTS_3(_condition, _reason, _exception_type) \
275  do { \
276  static_assert(std::is_base_of_v<std::exception, _exception_type>); \
277  rapidsmpf::detail::expects_impl( \
278  static_cast<bool>(_condition), (_reason), [](auto&& msg) { \
279  throw _exception_type{msg}; \
280  } \
281  ); \
282  } while (0)
283 
284 #define RAPIDSMPF_EXPECTS_2(_condition, _reason) \
285  RAPIDSMPF_EXPECTS_3(_condition, _reason, std::logic_error)
286 
306 #define RAPIDSMPF_FAIL(...) \
307  GET_RAPIDSMPF_FAIL_MACRO(__VA_ARGS__, RAPIDSMPF_FAIL_2, RAPIDSMPF_FAIL_1) \
308  (__VA_ARGS__)
309 
310 #define GET_RAPIDSMPF_FAIL_MACRO(_1, _2, NAME, ...) NAME
311 
312 #define RAPIDSMPF_FAIL_2(_what, _exception_type) \
313  throw _exception_type { \
314  rapidsmpf::detail::build_error_message((_what)) \
315  }
316 
317 #define RAPIDSMPF_FAIL_1(_what) RAPIDSMPF_FAIL_2(_what, std::logic_error)
318 
335 #define RAPIDSMPF_EXPECTS_FATAL(_condition, _reason) \
336  rapidsmpf::detail::expects_fatal_impl(static_cast<bool>(_condition), (_reason))
337 
352 #define RAPIDSMPF_FATAL(_reason) rapidsmpf::detail::fatal_impl((_reason))
353 
373 #define RAPIDSMPF_CUDA_TRY(...) \
374  GET_RAPIDSMPF_CUDA_TRY_MACRO( \
375  __VA_ARGS__, RAPIDSMPF_CUDA_TRY_2, RAPIDSMPF_CUDA_TRY_1 \
376  ) \
377  (__VA_ARGS__)
378 #define GET_RAPIDSMPF_CUDA_TRY_MACRO(_1, _2, NAME, ...) NAME
379 #define RAPIDSMPF_CUDA_TRY_2(_call, _exception_type) \
380  do { \
381  cudaError_t const error = (_call); \
382  if (cudaSuccess != error) { \
383  /* Clear the CUDA error state. The error is now represented */ \
384  /* by the thrown exception. */ \
385  cudaGetLastError(); \
386  throw _exception_type{rapidsmpf::detail::build_cuda_error_message(error)}; \
387  } \
388  } while (0)
389 #define RAPIDSMPF_CUDA_TRY_1(_call) RAPIDSMPF_CUDA_TRY_2(_call, rapidsmpf::cuda_error)
390 
405 #define RAPIDSMPF_CUDA_TRY_FATAL(_call) \
406  do { \
407  cudaError_t const error = (_call); \
408  if (cudaSuccess != error) { \
409  std::cerr << rapidsmpf::detail::build_cuda_error_message(error) \
410  << std::endl; \
411  std::terminate(); \
412  } \
413  } while (0)
414 
431 #define RAPIDSMPF_CUDA_TRY_ALLOC(...) \
432  GET_RAPIDSMPF_CUDA_TRY_ALLOC_MACRO( \
433  __VA_ARGS__, RAPIDSMPF_CUDA_TRY_ALLOC_2, RAPIDSMPF_CUDA_TRY_ALLOC_1 \
434  ) \
435  (__VA_ARGS__)
436 #define GET_RAPIDSMPF_CUDA_TRY_ALLOC_MACRO(_1, _2, NAME, ...) NAME
437 
438 #define RAPIDSMPF_CUDA_TRY_ALLOC_2(_call, num_bytes) \
439  do { \
440  cudaError_t const error = (_call); \
441  if (cudaSuccess != error) { \
442  /* Clear the CUDA error state. The error is now represented */ \
443  /* by the thrown exception. */ \
444  cudaGetLastError(); \
445  auto const msg = \
446  rapidsmpf::detail::build_cuda_alloc_error_message(error, (num_bytes)); \
447  if (cudaErrorMemoryAllocation == error) { \
448  throw rapidsmpf::out_of_memory{msg}; \
449  } \
450  throw rapidsmpf::bad_alloc{msg}; \
451  } \
452  } while (0)
453 
454 #define RAPIDSMPF_CUDA_TRY_ALLOC_1(_call) \
455  do { \
456  cudaError_t const error = (_call); \
457  if (cudaSuccess != error) { \
458  /* Clear the CUDA error state. The error is now represented */ \
459  /* by the thrown exception. */ \
460  cudaGetLastError(); \
461  auto const msg = rapidsmpf::detail::build_cuda_error_message(error); \
462  if (cudaErrorMemoryAllocation == error) { \
463  throw rapidsmpf::out_of_memory{msg}; \
464  } \
465  throw rapidsmpf::bad_alloc{msg}; \
466  } \
467  } while (0)
468 
469 } // namespace rapidsmpf
Exception thrown when a RapidsMPF allocation fails.
Definition: error.hpp:35
bad_alloc(char const *msg)
Construct a bad_alloc with the error message.
Definition: error.hpp:42
char const * what() const noexcept override
Return the explanatory string.
Definition: error.hpp:57
bad_alloc(std::string const &msg)
Construct a bad_alloc with the error message.
Definition: error.hpp:50
Exception thrown when RapidsMPF runs out of memory.
Definition: error.hpp:72
out_of_memory(std::string const &msg)
Construct an out_of_memory with the error message.
Definition: error.hpp:87
out_of_memory(char const *msg)
Construct an out_of_memory with the error message.
Definition: error.hpp:79
Exception thrown when a memory reservation fails in RapidsMPF.
Definition: error.hpp:100
reservation_error(char const *msg)
Construct a reservation_error with an error message.
Definition: error.hpp:107
reservation_error(std::string const &msg)
Construct a reservation_error with an error message.
Definition: error.hpp:115
std::string build_cuda_error_message(cudaError_t error, std::source_location const &loc=std::source_location::current())
Build a CUDA error message with source location information.
Definition: error.hpp:164
void fatal_impl(std::string_view reason, std::source_location const &loc=std::source_location::current()) noexcept
Implementation for RAPIDSMPF_FATAL.
Definition: error.hpp:235
constexpr void expects_fatal_impl(bool condition, std::string_view reason, std::source_location const &loc=std::source_location::current()) noexcept
Implementation for RAPIDSMPF_EXPECTS_FATAL.
Definition: error.hpp:219
constexpr void expects_impl(bool condition, std::string_view reason, ThrowFn &&throw_fn, std::source_location const &loc=std::source_location::current())
Core implementation for RAPIDSMPF_EXPECTS.
Definition: error.hpp:146
std::string build_error_message(std::string_view reason, std::source_location const &loc=std::source_location::current())
Build an error message with source location information.
Definition: error.hpp:127
std::string build_cuda_alloc_error_message(cudaError_t error, std::size_t num_bytes, std::source_location const &loc=std::source_location::current())
Build a CUDA allocation error message with source location information.
Definition: error.hpp:181
void fatal_error(std::string_view reason, std::source_location const &loc=std::source_location::current()) noexcept
Print a fatal error message and terminate.
Definition: error.hpp:203
RAPIDS Multi-Processor interfaces.
Definition: backend.hpp:13
Exception thrown when a CUDA error is encountered.
Definition: error.hpp:26