From 67d328a55078abda413ade3f1cf1ccd4380135f2 Mon Sep 17 00:00:00 2001 From: Simon Rozman Date: Sun, 1 Oct 2023 23:05:54 +0200 Subject: [PATCH] watchdog: add Signed-off-by: Simon Rozman --- UnitTests/UnitTests.vcxproj | 1 + UnitTests/UnitTests.vcxproj.filters | 3 + UnitTests/main.cpp | 2 + UnitTests/pch.hpp | 3 +- UnitTests/watchdog.cpp | 32 ++++++++++ include/stdex/watchdog.hpp | 92 +++++++++++++++++++++++++++++ 6 files changed, 132 insertions(+), 1 deletion(-) create mode 100644 UnitTests/watchdog.cpp create mode 100644 include/stdex/watchdog.hpp diff --git a/UnitTests/UnitTests.vcxproj b/UnitTests/UnitTests.vcxproj index 95931c154..33eac273d 100644 --- a/UnitTests/UnitTests.vcxproj +++ b/UnitTests/UnitTests.vcxproj @@ -128,6 +128,7 @@ + diff --git a/UnitTests/UnitTests.vcxproj.filters b/UnitTests/UnitTests.vcxproj.filters index d5a92f7e8..69db57137 100644 --- a/UnitTests/UnitTests.vcxproj.filters +++ b/UnitTests/UnitTests.vcxproj.filters @@ -39,6 +39,9 @@ Source Files + + Source Files + diff --git a/UnitTests/main.cpp b/UnitTests/main.cpp index 8ee1385dd..8267cf4b4 100644 --- a/UnitTests/main.cpp +++ b/UnitTests/main.cpp @@ -11,6 +11,7 @@ #include "sgml.cpp" #include "stream.cpp" #include "unicode.cpp" +#include "watchdog.cpp" #include int main(int argc, const char * argv[]) @@ -34,6 +35,7 @@ int main(int argc, const char * argv[]) UnitTests::unicode::str2wstr(); UnitTests::unicode::wstr2str(); UnitTests::unicode::charset_encoder(); + UnitTests::watchdog::test(); std::cout << "PASS\n"; return 0; } diff --git a/UnitTests/pch.hpp b/UnitTests/pch.hpp index b674b4bfd..b812d1642 100644 --- a/UnitTests/pch.hpp +++ b/UnitTests/pch.hpp @@ -1,4 +1,4 @@ -/* +/* SPDX-License-Identifier: MIT Copyright © 2023 Amebis */ @@ -23,6 +23,7 @@ #include #include #include +#include #include "compat.hpp" diff --git a/UnitTests/watchdog.cpp b/UnitTests/watchdog.cpp new file mode 100644 index 000000000..ec90f4e10 --- /dev/null +++ b/UnitTests/watchdog.cpp @@ -0,0 +1,32 @@ +/* + SPDX-License-Identifier: MIT + Copyright © 2023 Amebis +*/ + +#include "pch.hpp" + +using namespace std; +#ifdef _WIN32 +using namespace Microsoft::VisualStudio::CppUnitTestFramework; +#endif + +namespace UnitTests +{ + TEST_CLASS(watchdog) + { + public: + TEST_METHOD(test) + { + volatile bool wd_called = false; + stdex::watchdog wd( + std::chrono::milliseconds(100), [&] { wd_called = true; }); + for (int i = 0; i < 100; ++i) { + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + Assert::IsFalse(wd_called); + wd.reset(); + } + std::this_thread::sleep_for(std::chrono::milliseconds(300)); + Assert::IsTrue(wd_called); + } + }; +} \ No newline at end of file diff --git a/include/stdex/watchdog.hpp b/include/stdex/watchdog.hpp new file mode 100644 index 000000000..e899f281a --- /dev/null +++ b/include/stdex/watchdog.hpp @@ -0,0 +1,92 @@ +/* + SPDX-License-Identifier: MIT + Copyright © 2023 Amebis +*/ + +#pragma once + +#include "compat.hpp" +#include +#include +#include +#include +#include + +namespace stdex +{ + /// + /// Triggers callback if not reset frequently enough + /// + template + class watchdog + { + public: + /// + /// Starts the watchdog + /// + /// \param[in] timeout How long the watchdog is waiting for a reset + /// \param[in] callback The function watchdog calls on timeout + /// + watchdog(_In_ _Duration timeout, _In_ std::function callback) : + m_phase(0), + m_quit(false), + m_timeout(timeout), + m_callback(callback), + m_thread(run, std::ref(*this)) + {} + + /// + /// Stops the watchdog + /// + ~watchdog() + { + { + const std::lock_guard lk(m_mutex); + m_quit = true; + } + m_cv.notify_one(); + if (m_thread.joinable()) + m_thread.join(); + } + + /// + /// Resets the watchdog + /// + /// Must be called frequently enough not to timeout the watchdog + /// + void reset() + { + { + const std::lock_guard lk(m_mutex); + m_phase++; + } + m_cv.notify_one(); + } + + protected: + static void run(_Inout_ watchdog& wd) + { + for (;;) { + std::unique_lock lk(wd.m_mutex); + auto phase = wd.m_phase; + if (wd.m_cv.wait_for(lk, wd.m_timeout, [&] {return wd.m_quit || phase != wd.m_phase; })) { + if (wd.m_quit) + break; + } + else { + wd.m_callback(); + break; + } + } + } + + protected: + size_t m_phase; ///< A counter we are incrementing to keep the watchdog happy + bool m_quit; ///< Quit the watchdog + _Duration m_timeout; ///< How long the watchdog is waiting for a reset + std::function m_callback; ///< The function watchdog calls on timeout + std::mutex m_mutex; + std::condition_variable m_cv; + std::thread m_thread; + }; +}