error_handling

Runtime Exceptions

Conditions that are possible and can happen during runtime, and can be handled without crashing. They are recoverable.

Programming Errors

Things that should never happen, like an array out of bounds. They represent programming or logical errors. They are unrecoverable.

Most common are invariant violations:

  • Array index out of bounds

  • Null pointer dereference

  • Division by zero

  • Impossible state combinations (e.g., a mutex both locked and unlocked)

  • Violation of logical constraints (e.g., a negative count of items)

How to Handle Each Type of Error

Use exceptions when:

  1. Recovery is possible: The error condition might be handled and the program can continue execution

  2. Expected failure conditions: For situations like file not found or network timeouts

  3. Creating reusable libraries: Allows client code to handle errors in context-appropriate ways

  4. Propagating errors up the call stack: When the handling code is far from where the error occurs

  5. Constructor failures: Exceptions are the only clean way to handle initialization failures

Use LOG(FATAL) or CHECK when:

  1. Programming errors: Assertions, invariant violations, and "this should never happen" scenarios

  2. Unrecoverable states: When continuing execution would be unsafe or impossible

  3. Performance-critical code: Where exception handling overhead is unacceptable

  4. Core system functionality: Where simplicity and predictability are paramount

  5. Clear contract violations: Such as null pointers or invalid arguments that indicate bugs

Invariant violations are typically handled with mechanisms like CHECK(), assert(), or LOG(FATAL) since they represent programming errors that should be fixed rather than handled at runtime.

Exceptions are for exceptional but recoverable conditions

LOG(FATAL)/CHECK are for programming errors and invariant violations

In high-reliability systems, fatal crashes with clear error messages are often preferable to unpredictable behavior.

Fatal crashes are better for real-time code because:

  • Exception handling adds overhead

  • Exception handling is unpredictable; you don't want program flow to be very different based on exceptional circumstances. If something is very different, you prefer to crash.

Last updated