|
10 | 10 |
|
11 | 11 | #include "AssertFailed.h" |
12 | 12 | #include <iostream> |
| 13 | +#include <thread> |
13 | 14 | #include "backtrace.h" |
14 | 15 | #include "../logging/logging.h" |
15 | 16 |
|
16 | 17 | namespace cpputils { |
17 | 18 | namespace _assert { |
| 19 | + struct DisableAbortOnFailedAssertionRAII final { |
| 20 | + explicit DisableAbortOnFailedAssertionRAII() |
| 21 | + : thread_id_(std::this_thread::get_id()) { |
| 22 | + ++num_instances_; |
| 23 | + } |
| 24 | + |
| 25 | + ~DisableAbortOnFailedAssertionRAII() { |
| 26 | + if (thread_id_ != std::this_thread::get_id()) { |
| 27 | + using namespace logging; |
| 28 | + LOG(ERR, "DisableAbortOnFailedAssertionRAII instance must be destructed in the same thread that created it"); |
| 29 | + } |
| 30 | + --num_instances_; |
| 31 | + } |
| 32 | + |
| 33 | + static int num_instances() { |
| 34 | + return num_instances_; |
| 35 | + } |
| 36 | + |
| 37 | + private: |
| 38 | + static thread_local int num_instances_; // initialized to zero in assert.cpp |
| 39 | + |
| 40 | + std::thread::id thread_id_; |
| 41 | + }; |
| 42 | + |
18 | 43 | inline std::string format(const char *expr, const std::string &message, const char *file, int line) { |
19 | 44 | std::string result = std::string()+"Assertion ["+expr+"] failed in "+file+":"+std::to_string(line)+": "+message+"\n\n" + backtrace(); |
20 | 45 | return result; |
21 | 46 | } |
22 | 47 |
|
23 | 48 | inline void assert_fail_release [[noreturn]] (const char *expr, const std::string &message, const char *file, int line) { |
24 | | - auto msg = format(expr, message, file, line); |
25 | 49 | using namespace logging; |
| 50 | + auto msg = format(expr, message, file, line); |
26 | 51 | LOG(ERR, msg); |
27 | 52 | throw AssertFailed(msg); |
28 | 53 | } |
29 | 54 |
|
30 | 55 | inline void assert_fail_debug [[noreturn]] (const char *expr, const std::string &message, const char *file, int line) { |
31 | 56 | using namespace logging; |
32 | | - LOG(ERR, format(expr, message, file, line)); |
33 | | - abort(); |
| 57 | + auto msg = format(expr, message, file, line); |
| 58 | + LOG(ERR, msg); |
| 59 | + if (DisableAbortOnFailedAssertionRAII::num_instances() > 0) { |
| 60 | + throw AssertFailed(msg); |
| 61 | + } else { |
| 62 | + abort(); |
| 63 | + } |
34 | 64 | } |
35 | 65 | } |
36 | 66 | } |
37 | 67 |
|
38 | 68 | #ifdef NDEBUG |
39 | | -//TODO Check whether disabling assertions in prod affects speed. |
40 | | -# define ASSERT(expr, msg) (void)((expr) || (cpputils::_assert::assert_fail_release(#expr, msg, __FILE__, __LINE__),0)) |
| 69 | + //TODO Check whether disabling assertions in prod affects speed. |
| 70 | + #define ASSERT(expr, msg) (void)((expr) || (cpputils::_assert::assert_fail_release(#expr, msg, __FILE__, __LINE__),0)) |
41 | 71 | #else |
42 | | -# define ASSERT(expr, msg) (void)((expr) || (cpputils::_assert::assert_fail_debug(#expr, msg, __FILE__, __LINE__),0)) |
| 72 | + #define ASSERT(expr, msg) (void)((expr) || (cpputils::_assert::assert_fail_debug(#expr, msg, __FILE__, __LINE__),0)) |
43 | 73 | #endif |
44 | 74 |
|
45 | 75 | #endif |
0 commit comments