From 0b6810aca783d2fb08e11929672757d73f1b9a7e Mon Sep 17 00:00:00 2001 From: Tristan Brindle Date: Mon, 9 Sep 2024 19:18:59 +0100 Subject: [PATCH] Add fail_fast runtime error policy This adds a new fail_fast runtime error policy to go with our existing terminate and unwind policies. As the name suggests, fail_fast is intended to tear down the process in the fastest way possible should a runtime error occur. It uses `__builtin_trap()` on GCC and Clang, the `__fastfail()` intrinsic on MSVC, and otherwise falls back to `std::abort()`. Unlike the terminate policy, it never attempts to print a message to stderr. --- include/flux/core/assert.hpp | 61 +++++++++++++++++++++++++++++------- include/flux/core/config.hpp | 8 +++-- 2 files changed, 55 insertions(+), 14 deletions(-) diff --git a/include/flux/core/assert.hpp b/include/flux/core/assert.hpp index 5c1416be..76a44982 100644 --- a/include/flux/core/assert.hpp +++ b/include/flux/core/assert.hpp @@ -14,6 +14,15 @@ #include #include +#if defined(__has_builtin) +# if __has_builtin(__builtin_trap) +# define FLUX_HAS_BUILTIN_TRAP 1 +# endif +#elif defined(_MSC_VER) +# include +# define FLUX_HAS_FASTFAIL 1 +#endif + namespace flux { FLUX_EXPORT @@ -24,21 +33,49 @@ struct unrecoverable_error : std::logic_error { namespace detail { struct runtime_error_fn { +private: [[noreturn]] - void operator()(char const* msg, - std::source_location loc = std::source_location::current()) const + static inline void fail_fast() { - if constexpr (config::on_error == error_policy::unwind) { - char buf[1024]; - std::snprintf(buf, 1024, "%s:%u: Fatal error: %s", - loc.file_name(), loc.line(), msg); - throw unrecoverable_error(buf); +#if FLUX_HAS_BUILTIN_TRAP + __builtin_trap(); +#elif FLUX_HAS_FASTFAIL + __fastfail(7); // FAST_FAIL_FATAL_APP_EXIT +#else + std::abort(); +#endif + } + + [[noreturn]] + static void unwind(const char* msg, std::source_location loc) + { + char buf[1024]; + std::snprintf(buf, 1024, "%s:%u: Fatal error: %s", + loc.file_name(), loc.line(), msg); + throw unrecoverable_error(buf); + } + + [[noreturn]] + static void terminate(const char* msg, std::source_location loc) + { + if constexpr (config::print_error_on_terminate) { + std::fprintf(stderr, "%s:%u: Fatal error: %s\n", + loc.file_name(), loc.line(), msg); + } + std::terminate(); + } + +public: + [[noreturn]] + inline void operator()(char const* msg, + std::source_location loc = std::source_location::current()) const + { + if constexpr (config::on_error == error_policy::fail_fast) { + fail_fast(); + } else if constexpr (config::on_error == error_policy::unwind) { + unwind(msg, loc); } else { - if constexpr (config::print_error_on_terminate) { - std::fprintf(stderr, "%s:%u: Fatal error: %s\n", - loc.file_name(), loc.line(), msg); - } - std::terminate(); + terminate(msg, loc); } } }; diff --git a/include/flux/core/config.hpp b/include/flux/core/config.hpp index 322b663e..92d7af72 100644 --- a/include/flux/core/config.hpp +++ b/include/flux/core/config.hpp @@ -13,7 +13,8 @@ #include #define FLUX_ERROR_POLICY_TERMINATE 1 -#define FLUX_ERROR_POLICY_UNWIND 2 +#define FLUX_ERROR_POLICY_UNWIND 2 +#define FLUX_ERROR_POLICY_FAIL_FAST 3 #define FLUX_OVERFLOW_POLICY_ERROR 10 #define FLUX_OVERFLOW_POLICY_WRAP 11 @@ -47,6 +48,8 @@ # define FLUX_ERROR_POLICY FLUX_ERROR_POLICY_TERMINATE #elif defined(FLUX_UNWIND_ON_ERROR) # define FLUX_ERROR_POLICY FLUX_ERROR_POLICY_UNWIND +#elif defined(FLUX_FAIL_FAST_ON_ERROR) +# define FLUX_ERROR_POLICY FLUX_ERROR_POLICY_FAIL_FAST #else # define FLUX_ERROR_POLICY FLUX_DEFAULT_ERROR_POLICY #endif // FLUX_TERMINATE_ON_ERROR @@ -123,7 +126,8 @@ namespace flux { FLUX_EXPORT enum class error_policy { terminate = FLUX_ERROR_POLICY_TERMINATE, - unwind = FLUX_ERROR_POLICY_UNWIND + unwind = FLUX_ERROR_POLICY_UNWIND, + fail_fast = FLUX_ERROR_POLICY_FAIL_FAST }; FLUX_EXPORT