diff --git a/include/silo/common/panic.h b/include/silo/common/panic.h index 302422529..f96a7341e 100644 --- a/include/silo/common/panic.h +++ b/include/silo/common/panic.h @@ -4,19 +4,55 @@ namespace silo::common { -void panic(const std::string& msg, const char* file, int line); +/// ** This is the basic building block for the various panicking +/// features offered in this file. You probably want to use the +/// `PANIC` macro instead of `panic` directly, to get format +/// functionality and file/line number information! ** +/// +/// `panic` stops execution because of a situation that the author of +/// the program expected to never happen, i.e. that is against +/// expectations of internal consistency, and means that there is +/// either a bug, or a misunderstanding of the consistency rules on +/// behalf of the programmer. +/// +/// By default, `panic` throws a `std::runtime_error` exception, so +/// that the program can continue by e.g. abandoning the current http +/// connection but continue servicing other and future +/// connections. OTOH, to make debugging via gdb or core dumps +/// possible even when the code that captures exceptions can't be +/// disabled, `panic` can be instructed at runtime to call `abort` +/// instead by setting the `SILO_PANIC` environment variable to the +/// string `abort` (with any other value, or when unset, panic +/// silently throws the mentioned exception instead). +[[noreturn]] void panic(const std::string& msg, const char* file, int line); /// Passes arguments to `fmt::format` (at least a format string -/// argument is required), then if the `DEBUG` environment variable is -/// set to anything else than the string "0", the resulting string is -/// printed to stderr and `abort` is called, otherwise a -/// `std::runtime_error` is thrown (details subject to -/// change). `PANIC` should only be used in situations that can never -/// occur unless there is a bug; the aim, besides allowing the -/// programmer to declare clearly what constitutes buggy behaviour, is -/// to allow these situations to be debugged via a debugger like gdb -/// without needing contortions like setting breakpoints, or also to -/// allow the collection of core dumps of these situations. +/// argument is required) and adds file and line information, then +/// calls `panic`. #define PANIC(...) silo::common::panic(fmt::format(__VA_ARGS__), __FILE__, __LINE__) +/// Denotes a place that theoretically can't be reached. Follows the +/// same path as `PANIC` when reached. +#define UNREACHABLE() silo::common::unreachable(__FILE__, __LINE__) + +[[noreturn]] void unreachable(const char* file, int line); + +/// Denotes a missing implementation. Follows the same path as `PANIC` +/// when reached. +#define UNIMPLEMENTED() silo::common::unimplemented(__FILE__, __LINE__) + +[[noreturn]] void unimplemented(const char* file, int line); + +/// Asserts that the expression `e` evaluates to true. On failure +/// calls `panic` with the stringification of the code `e` and +/// file/line information. +#define ASSERT(e) \ + do { \ + if (!(e)) { \ + silo::common::assertFailure(#e, __FILE__, __LINE__); \ + } \ + } while (0) + +[[noreturn]] void assertFailure(const char* msg, const char* file, int line); + } // namespace silo::common diff --git a/src/silo/common/panic.cpp b/src/silo/common/panic.cpp index 47412b9d7..ff0da94ea 100644 --- a/src/silo/common/panic.cpp +++ b/src/silo/common/panic.cpp @@ -1,26 +1,28 @@ #include "silo/common/panic.h" #include +#include #include namespace silo::common { namespace { -bool is0(const char* str) { - return str[0] == '0' && str[1] == '\0'; -} -} // namespace -void panic(const std::string& msg, const char* file, int line) { - const char* env = getenv("DEBUG"); - bool debug; +[[noreturn]] void panic( + const std::string& prefix, + const std::string& msg, + const char* file, + int line +) { + const char* env = getenv("SILO_PANIC"); + bool do_abort; if (env) { - debug = !is0(env); + do_abort = strcmp(env, "abort") == 0; } else { - debug = false; + do_abort = false; } - auto full_msg = fmt::format("PANIC: {} at {}:{}", msg, file, line); - if (debug) { + auto full_msg = fmt::format("{}{} at {}:{}", prefix, msg, file, line); + if (do_abort) { std::cerr << full_msg << "\n" << std::flush; abort(); } else { @@ -28,4 +30,27 @@ void panic(const std::string& msg, const char* file, int line) { } } +} // namespace + +[[noreturn]] void panic(const std::string& msg, const char* file, int line) { + panic("PANIC: ", msg, file, line); +} + +[[noreturn]] void unreachable(const char* file, int line) { + panic( + "UNREACHABLE: ", + "Please report this as a bug in SILO: this code should never be reachable", + file, + line + ); +} + +[[noreturn]] void unimplemented(const char* file, int line) { + panic("UNIMPLEMENTED: ", "This execution path is not implemented yet", file, line); +} + +[[noreturn]] void assertFailure(const char* msg, const char* file, int line) { + panic("ASSERT failure: ", msg, file, line); +} + } // namespace silo::common