diff --git a/src/common/include/display_device/logging.h b/src/common/include/display_device/logging.h index f22a043a..d394db62 100644 --- a/src/common/include/display_device/logging.h +++ b/src/common/include/display_device/logging.h @@ -124,7 +124,7 @@ namespace display_device { /** * @brief Write out the accumulated output. */ - virtual ~LogWriter(); + virtual ~LogWriter() noexcept; /** * @brief Stream value to the buffer. diff --git a/src/common/include/display_device/retry_scheduler.h b/src/common/include/display_device/retry_scheduler.h index 6fae59fd..a0167702 100644 --- a/src/common/include/display_device/retry_scheduler.h +++ b/src/common/include/display_device/retry_scheduler.h @@ -43,7 +43,7 @@ namespace display_device { /** * @brief Executes cleanup logic if scheduler stop was requested. */ - ~SchedulerStopToken(); + ~SchedulerStopToken() noexcept; /** * @brief Request the scheduler to be stopped. diff --git a/src/common/logging.cpp b/src/common/logging.cpp index a8c55912..cc51baa4 100644 --- a/src/common/logging.cpp +++ b/src/common/logging.cpp @@ -11,6 +11,7 @@ // system includes #include +#include #include #include #include @@ -29,6 +30,11 @@ namespace display_device { } return {}; } + + void reportLogWriterException(const std::exception &error) noexcept { + std::cerr << "Exception thrown while writing a log message. Error:\n" + << error.what() << '\n'; + } } // namespace Logger &Logger::get() { @@ -115,7 +121,11 @@ namespace display_device { LogWriter::LogWriter(const Logger::LogLevel log_level): m_log_level {log_level} {} - LogWriter::~LogWriter() { - Logger::get().write(m_log_level, m_buffer.str()); + LogWriter::~LogWriter() noexcept { + try { + Logger::get().write(m_log_level, m_buffer.str()); + } catch (const std::exception &error) { + reportLogWriterException(error); + } } } // namespace display_device diff --git a/src/common/retry_scheduler.cpp b/src/common/retry_scheduler.cpp index f8368980..e218de9f 100644 --- a/src/common/retry_scheduler.cpp +++ b/src/common/retry_scheduler.cpp @@ -5,14 +5,29 @@ // header include #include "display_device/retry_scheduler.h" +// system includes +#include +#include + namespace display_device { + namespace { + void reportSchedulerStopTokenException(const std::exception &error) noexcept { + std::cerr << "Exception thrown in SchedulerStopToken cleanup. Ignoring. Error:\n" + << error.what() << '\n'; + } + } // namespace + SchedulerStopToken::SchedulerStopToken(std::function cleanup): m_cleanup {std::move(cleanup)} { } - SchedulerStopToken::~SchedulerStopToken() { + SchedulerStopToken::~SchedulerStopToken() noexcept { if (m_stop_requested && m_cleanup) { - m_cleanup(); + try { + m_cleanup(); + } catch (const std::exception &error) { + reportSchedulerStopTokenException(error); + } } } diff --git a/tests/unit/general/test_logging.cpp b/tests/unit/general/test_logging.cpp index 3664fa04..74576f9e 100644 --- a/tests/unit/general/test_logging.cpp +++ b/tests/unit/general/test_logging.cpp @@ -1,8 +1,18 @@ +// system includes +#include + // local includes #include "display_device/logging.h" #include "fixtures/fixtures.h" namespace { + class LogWriterDestructorTestException final: public std::exception { + public: + [[nodiscard]] const char *what() const noexcept override { + return "Get rekt!"; + } + }; + // Specialized TEST macro(s) for this test file #define TEST_S(...) DD_MAKE_TEST(TEST, LoggingTest, __VA_ARGS__) } // namespace @@ -206,3 +216,15 @@ TEST_S(LogMacroDisablesStreamChain) { EXPECT_EQ(output_logged, true); EXPECT_EQ(some_function_invoked, true); } + +TEST_S(LogWriterDestructorDoesNotPropagateCallbackExceptions) { + using level = display_device::Logger::LogLevel; + auto &logger {display_device::Logger::get()}; + + logger.setLogLevel(level::info); + logger.setCustomCallback([](auto, auto) { + throw LogWriterDestructorTestException {}; + }); + + EXPECT_NO_THROW(DD_LOG(info) << "Hello World!"); +} diff --git a/tests/unit/general/test_retry_scheduler.cpp b/tests/unit/general/test_retry_scheduler.cpp index eb80fd7f..ad40847d 100644 --- a/tests/unit/general/test_retry_scheduler.cpp +++ b/tests/unit/general/test_retry_scheduler.cpp @@ -1,5 +1,7 @@ // system includes +#include #include +#include // local includes #include "display_device/retry_scheduler.h" @@ -20,6 +22,13 @@ namespace { void constMethod() const { /* noop */ } }; + class SchedulerStopTokenTestException final: public std::exception { + public: + [[nodiscard]] const char *what() const noexcept override { + return "Get rekt!"; + } + }; + // Some threads wake up a little earlier than expected, so we round the lower bound to // 99% of the expected value to stabilize the tests int roundTo99(int value) { @@ -636,6 +645,12 @@ TEST_F_S(ThreadCleanupInDestructor) { } TEST_F_S(SchedulerStopToken, DestructorNoThrow) { + EXPECT_NO_THROW({ + display_device::SchedulerStopToken token {[]() { + throw SchedulerStopTokenTestException {}; + }}; + token.requestStop(); + }); EXPECT_NO_THROW({ display_device::SchedulerStopToken token {[]() { }};