Celeritas  0.5.0-86+4a8eea4
Classes | Macros | Enumerations | Functions
Assert.hh File Reference

Macros, exceptions, and helpers for assertions and error handling. More...

#include <stdexcept>
#include <string>
#include <ostream>
#include <sstream>
#include "corecel/Config.hh"
#include "Macros.hh"

Classes

struct  celeritas::DebugErrorDetails
 Detailed properties of a debug assertion failure. More...
 
struct  celeritas::RuntimeErrorDetails
 Detailed properties of a runtime error. More...
 
class  celeritas::DebugError
 Error thrown by Celeritas assertions. More...
 
class  celeritas::RuntimeError
 Error thrown by working code from unexpected runtime conditions. More...
 
class  celeritas::RichContextException
 Base class for writing arbitrary exception context to JSON. More...
 

Macros

#define CELER_DEBUG_FAIL(MSG, WHICH)
 Throw a debug assertion regardless of the CELERITAS_DEBUG setting. More...
 
#define CELER_RUNTIME_THROW(WHICH, WHAT, COND)
 
#define CELER_EXPECT(COND)   CELER_NOASSERT_(COND)
 Precondition debug assertion macro. More...
 
#define CELER_ASSERT(COND)   CELER_NOASSERT_(COND)
 Internal debug assertion macro. More...
 
#define CELER_ENSURE(COND)   CELER_NOASSERT_(COND)
 Postcondition debug assertion macro. More...
 
#define CELER_ASSUME(COND)   CELER_NDEBUG_ASSUME_(COND)
 Always-on compiler assumption. More...
 
#define CELER_ASSERT_UNREACHABLE()   ::celeritas::unreachable()
 Throw an assertion if the code point is reached. More...
 
#define CELER_VALIDATE(COND, MSG)
 Always-on runtime assertion macro. More...
 
#define CELER_NOT_CONFIGURED(WHAT)
 Assert if the code point is reached because an optional feature is disabled. More...
 
#define CELER_NOT_IMPLEMENTED(WHAT)    CELER_RUNTIME_THROW(::celeritas::RuntimeError::not_impl_err_str, WHAT, {})
 Assert if the code point is reached because a feature has yet to be fully implemented. More...
 
#define CELER_CUDA_CALL(STATEMENT)
 When CUDA support is enabled, execute the wrapped statement and throw a RuntimeError if it fails. More...
 
#define CELER_HIP_CALL(STATEMENT)
 When HIP support is enabled, execute the wrapped statement and throw a RuntimeError if it fails. More...
 
#define CELER_DEVICE_CALL_PREFIX(STMT)
 Prepend the argument with "cuda" or "hip" and call with the appropriate checking statement as above. More...
 
#define CELER_DEVICE_CHECK_ERROR()   CELER_DEVICE_CALL_PREFIX(PeekAtLastError())
 After a kernel launch or other call, check that no CUDA errors have occurred. More...
 
#define CELER_MPI_CALL(STATEMENT)
 When MPI support is enabled, execute the wrapped statement and throw a RuntimeError if it fails. More...
 

Enumerations

enum class  celeritas::DebugErrorType {
  precondition , internal , unreachable , postcondition ,
  assumption
}
 

Functions

void celeritas::unreachable ()
 Invoke undefined behavior.
 
char const * celeritas::to_cstring (DebugErrorType which)
 Get a human-readable string describing a debug error.
 
std::string celeritas::mpi_error_to_string (int errorcode)
 Get an MPI error string.
 

Detailed Description

Macros, exceptions, and helpers for assertions and error handling.

This defines host- and device-compatible assertion macros that are toggled on the CELERITAS_DEBUG and CELERITAS_DEVICE_DEBUG configure macros.

Macro Definition Documentation

◆ CELER_ASSERT

#define CELER_ASSERT (   COND)    CELER_NOASSERT_(COND)

Internal debug assertion macro.

This replaces standard assert usage.

◆ CELER_ASSERT_UNREACHABLE

#define CELER_ASSERT_UNREACHABLE ( )    ::celeritas::unreachable()

Throw an assertion if the code point is reached.

When debug assertions are turned off, this changes to a compiler hint that improves optimization (and may force the coded to exit uncermoniously if the point is encountered, rather than continuing on with undefined behavior).

◆ CELER_ASSUME

#define CELER_ASSUME (   COND)    CELER_NDEBUG_ASSUME_(COND)

Always-on compiler assumption.

This should be used very rarely: you should make sure the resulting assembly is simplified in optimize mode from using the assumption. For example, sometimes informing the compiler of an assumption can reduce code bloat by skipping standard library exception handling code (e.g. in std::visit by assuming !var_obj.valueless_by_exception() ).

◆ CELER_CUDA_CALL

#define CELER_CUDA_CALL (   STATEMENT)
Value:
do \
{ \
CELER_NOT_CONFIGURED("CUDA"); \
CELER_DISCARD(CorecelDeviceRuntimeApiHh) \
} while (0)
int const CorecelDeviceRuntimeApiHh
Declare a dummy variable to be referenced in disabled CELER_BLAH calls.

When CUDA support is enabled, execute the wrapped statement and throw a RuntimeError if it fails.

If CUDA is disabled, throw an unconfigured assertion.

If it fails, we call cudaGetLastError to clear the error code. Note that this will not clear the code in a few fatal error cases (kernel assertion failure, invalid memory access) and all subsequent CUDA calls will fail.

CELER_CUDA_CALL(cudaMalloc(&ptr_gpu, 100 * sizeof(float)));
CELER_CUDA_CALL(cudaDeviceSynchronize());
#define CELER_CUDA_CALL(STATEMENT)
When CUDA support is enabled, execute the wrapped statement and throw a RuntimeError if it fails.
Definition: Assert.hh:282
Note
A file that uses this macro must include corecel/DeviceRuntimeApi.hh .

◆ CELER_DEBUG_FAIL

#define CELER_DEBUG_FAIL (   MSG,
  WHICH 
)
Value:
do \
{ \
CELER_DEBUG_THROW_(MSG, WHICH); \
::celeritas::unreachable(); \
} while (0)

Throw a debug assertion regardless of the CELERITAS_DEBUG setting.

This is used internally but is also useful for catching subtle programming errors in downstream code.

◆ CELER_DEVICE_CALL_PREFIX

#define CELER_DEVICE_CALL_PREFIX (   STMT)
Value:
do \
{ \
CELER_NOT_CONFIGURED("CUDA or HIP"); \
CELER_DISCARD(CorecelDeviceRuntimeApiHh) \
} while (0)

Prepend the argument with "cuda" or "hip" and call with the appropriate checking statement as above.

Example:

CELER_DEVICE_CALL_PREFIX(Malloc(&ptr_gpu, 100 * sizeof(float)));
CELER_DEVICE_CALL_PREFIX(DeviceSynchronize());
#define CELER_DEVICE_CALL_PREFIX(STMT)
Prepend the argument with "cuda" or "hip" and call with the appropriate checking statement as above.
Definition: Assert.hh:351
Note
A file that uses this macro must include corecel/DeviceRuntimeApi.hh . The CorecelDeviceRuntimeApiHh declaration enforces this when CUDA/HIP are disabled.

◆ CELER_DEVICE_CHECK_ERROR

#define CELER_DEVICE_CHECK_ERROR ( )    CELER_DEVICE_CALL_PREFIX(PeekAtLastError())

After a kernel launch or other call, check that no CUDA errors have occurred.

This is also useful for checking success after external CUDA libraries have been called.

◆ CELER_ENSURE

#define CELER_ENSURE (   COND)    CELER_NOASSERT_(COND)

Postcondition debug assertion macro.

Use to "ensure" that return values or side effects are as expected when leaving a function.

◆ CELER_EXPECT

#define CELER_EXPECT (   COND)    CELER_NOASSERT_(COND)

Precondition debug assertion macro.

We "expect" that the input values or initial state satisfy a precondition, and we throw exception in debug mode if they do not.

◆ CELER_HIP_CALL

#define CELER_HIP_CALL (   STATEMENT)
Value:
do \
{ \
CELER_NOT_CONFIGURED("HIP"); \
CELER_DISCARD(CorecelDeviceRuntimeApiHh) \
} while (0)

When HIP support is enabled, execute the wrapped statement and throw a RuntimeError if it fails.

If HIP is disabled, throw an unconfigured assertion.

If it fails, we call hipGetLastError to clear the error code.

CELER_HIP_CALL(hipMalloc(&ptr_gpu, 100 * sizeof(float)));
CELER_HIP_CALL(hipDeviceSynchronize());
#define CELER_HIP_CALL(STATEMENT)
When HIP support is enabled, execute the wrapped statement and throw a RuntimeError if it fails.
Definition: Assert.hh:321
Note
A file that uses this macro must include corecel/DeviceRuntimeApi.hh . The CorecelDeviceRuntimeApiHh declaration enforces this when HIP is disabled.

◆ CELER_MPI_CALL

#define CELER_MPI_CALL (   STATEMENT)
Value:
do \
{ \
CELER_NOT_CONFIGURED("MPI"); \
} while (0)

When MPI support is enabled, execute the wrapped statement and throw a RuntimeError if it fails.

If MPI is disabled, throw an unconfigured assertion.

Note
A file that uses this macro must include mpi.h.

◆ CELER_NOT_CONFIGURED

#define CELER_NOT_CONFIGURED (   WHAT)
Value:
CELER_RUNTIME_THROW( \
static char const not_config_err_str[]
Definition: Assert.hh:496

Assert if the code point is reached because an optional feature is disabled.

This generally should be used for the constructors of dummy class definitions in, e.g., Foo.nocuda.cc:

Foo::Foo()
{
}
#define CELER_NOT_CONFIGURED(WHAT)
Assert if the code point is reached because an optional feature is disabled.
Definition: Assert.hh:244

◆ CELER_NOT_IMPLEMENTED

#define CELER_NOT_IMPLEMENTED (   WHAT)     CELER_RUNTIME_THROW(::celeritas::RuntimeError::not_impl_err_str, WHAT, {})

Assert if the code point is reached because a feature has yet to be fully implemented.

This placeholder is so that code paths can be "declared but not defined" and implementations safely postponed in a greppable manner. This should not be used to define "unused" overrides for virtual classes. A correct use case would be:

if (z > AtomicNumber{26})
{
CELER_NOT_IMPLEMENTED("physics for heavy nuclides");
}
#define CELER_NOT_IMPLEMENTED(WHAT)
Assert if the code point is reached because a feature has yet to be fully implemented.
Definition: Assert.hh:247

◆ CELER_RUNTIME_THROW

#define CELER_RUNTIME_THROW (   WHICH,
  WHAT,
  COND 
)
Value:
throw ::celeritas::RuntimeError({ \
WHICH, \
WHAT, \
COND, \
__FILE__, \
__LINE__, \
})

◆ CELER_VALIDATE

#define CELER_VALIDATE (   COND,
  MSG 
)
Value:
do \
{ \
if (CELER_UNLIKELY(!(COND))) \
{ \
std::ostringstream celer_runtime_msg_; \
celer_runtime_msg_ MSG; \
CELER_RUNTIME_THROW( \
celer_runtime_msg_.str(), \
#COND); \
} \
} while (0)
#define CELER_UNLIKELY(COND)
Mark the result of this condition to be "unlikely".
Definition: Macros.hh:97
static char const validate_err_str[]
Definition: Assert.hh:495

Always-on runtime assertion macro.

This can check user input and input data consistency, and will raise RuntimeError on failure with a descriptive error message that is streamed as the second argument. This macro cannot be used in device -annotated code.

The error message should read:

 "<PROBLEM> (<WHY IT'S A PROBLEM>) <SUGGESTION>?"

Examples with correct casing and punctuation:

  • "failed to open '{filename}' (should contain relaxation data)"
  • "unexpected end of file '{filename}' (data is inconsistent with boundaries)"
  • "MPI was not initialized (needed to construct a communicator). Maybe set the environment variable CELER_DISABLE_PARALLEL=1 to disable externally?" - "invalid min_range={opts.min_range} (must be positive)"

This looks in pracice like:

CELER_VALIDATE(file_stream,
<< "failed to open '" << filename
<< "' (should contain relaxation data)");
#define CELER_VALIDATE(COND, MSG)
Always-on runtime assertion macro.
Definition: Assert.hh:227

An always-on debug-type assertion without a detailed message can be constructed by omitting the stream (but leaving the comma):

CELER_VALIDATE(file_stream,);

Enumeration Type Documentation

◆ DebugErrorType

Enumerator
precondition 

Precondition contract violation.

internal 

Internal assertion check failure.

unreachable 

Internal assertion: unreachable code path.

postcondition 

Postcondition contract violation.

assumption 

"Assume" violation