From 9cc614d096db4b7c1de6baaa055ca6f741aa4370 Mon Sep 17 00:00:00 2001 From: vaxerski <43317083+vaxerski@users.noreply.github.com> Date: Fri, 29 Sep 2023 16:38:13 +0100 Subject: internal: add a watchdog a watchdog will abort processing a signal if a timeout specified via debug:watchdog_timeout is reached. --- src/Compositor.cpp | 11 ++++++++++ src/Compositor.hpp | 1 + src/config/ConfigManager.cpp | 1 + src/helpers/WLListener.cpp | 8 +++++++- src/helpers/Watchdog.cpp | 49 ++++++++++++++++++++++++++++++++++++++++++++ src/helpers/Watchdog.hpp | 30 +++++++++++++++++++++++++++ 6 files changed, 99 insertions(+), 1 deletion(-) create mode 100644 src/helpers/Watchdog.cpp create mode 100644 src/helpers/Watchdog.hpp diff --git a/src/Compositor.cpp b/src/Compositor.cpp index 1cb378db..e114e934 100644 --- a/src/Compositor.cpp +++ b/src/Compositor.cpp @@ -34,6 +34,13 @@ void handleUnrecoverableSignal(int sig) { abort(); } +void handleUserSignal(int sig) { + if (sig == SIGUSR1) { + // means we have to unwind a timed out event + throw std::exception(); + } +} + CCompositor::CCompositor() { m_iHyprlandPID = getpid(); @@ -70,6 +77,8 @@ CCompositor::CCompositor() { setRandomSplash(); Debug::log(LOG, "\nCurrent splash: {}\n\n", m_szCurrentSplash); + + g_pWatchdog = std::make_unique(); } CCompositor::~CCompositor() { @@ -92,6 +101,7 @@ CCompositor::~CCompositor() { g_pAnimationManager.reset(); g_pKeybindManager.reset(); g_pHookSystem.reset(); + g_pWatchdog.reset(); } void CCompositor::setRandomSplash() { @@ -112,6 +122,7 @@ void CCompositor::initServer() { wl_event_loop_add_signal(m_sWLEventLoop, SIGTERM, handleCritSignal, nullptr); signal(SIGSEGV, handleUnrecoverableSignal); signal(SIGABRT, handleUnrecoverableSignal); + signal(SIGUSR1, handleUserSignal); //wl_event_loop_add_signal(m_sWLEventLoop, SIGINT, handleCritSignal, nullptr); initManagers(STAGE_PRIORITY); diff --git a/src/Compositor.hpp b/src/Compositor.hpp index 99839b06..86460211 100644 --- a/src/Compositor.hpp +++ b/src/Compositor.hpp @@ -27,6 +27,7 @@ #include "render/OpenGL.hpp" #include "hyprerror/HyprError.hpp" #include "plugins/PluginSystem.hpp" +#include "helpers/Watchdog.hpp" enum eManagersInitStage { diff --git a/src/config/ConfigManager.cpp b/src/config/ConfigManager.cpp index fc7a9e3e..9f696aca 100644 --- a/src/config/ConfigManager.cpp +++ b/src/config/ConfigManager.cpp @@ -134,6 +134,7 @@ void CConfigManager::setDefaultVars() { configValues["debug:damage_tracking"].intValue = DAMAGE_TRACKING_FULL; configValues["debug:manual_crash"].intValue = 0; configValues["debug:suppress_errors"].intValue = 0; + configValues["debug:watchdog_timeout"].intValue = 5; configValues["decoration:rounding"].intValue = 0; configValues["decoration:blur:enabled"].intValue = 1; diff --git a/src/helpers/WLListener.cpp b/src/helpers/WLListener.cpp index 3cb051b7..cace665d 100644 --- a/src/helpers/WLListener.cpp +++ b/src/helpers/WLListener.cpp @@ -6,7 +6,13 @@ void handleWrapped(wl_listener* listener, void* data) { CHyprWLListener::SWrapper* pWrap = wl_container_of(listener, pWrap, m_sListener); - pWrap->m_pSelf->emit(data); + g_pWatchdog->startWatching(); + + try { + pWrap->m_pSelf->emit(data); + } catch (std::exception& e) { Debug::log(ERR, "Listener {} timed out and was killed by Watchdog!!!", (uintptr_t)listener); } + + g_pWatchdog->endWatching(); } CHyprWLListener::CHyprWLListener(wl_signal* pSignal, std::function callback, void* pOwner) { diff --git a/src/helpers/Watchdog.cpp b/src/helpers/Watchdog.cpp new file mode 100644 index 00000000..92f20dfb --- /dev/null +++ b/src/helpers/Watchdog.cpp @@ -0,0 +1,49 @@ +#include "Watchdog.hpp" +#include +#include "config/ConfigManager.hpp" + +CWatchdog::CWatchdog() { + m_iMainThreadPID = pthread_self(); + + m_pWatchdog = std::make_unique([this] { + static auto* const PTIMEOUT = &g_pConfigManager->getConfigValuePtr("debug:watchdog_timeout")->intValue; + + while (1337) { + std::unique_lock lk(m_mWatchdogMutex); + + if (!m_bWillWatch) + m_cvWatchdogCondition.wait(lk, [this] { return m_bNotified; }); + else { + if (m_cvWatchdogCondition.wait_for(lk, std::chrono::milliseconds((int)(*PTIMEOUT * 1000.0)), [this] { return m_bNotified; }) == false) + pthread_kill(m_iMainThreadPID, SIGUSR1); + } + + m_bWatching = false; + m_bNotified = false; + } + }); + + m_pWatchdog->detach(); +} + +void CWatchdog::startWatching() { + static auto* const PTIMEOUT = &g_pConfigManager->getConfigValuePtr("debug:watchdog_timeout")->intValue; + + if (*PTIMEOUT == 0) + return; + + m_tTriggered = std::chrono::high_resolution_clock::now(); + m_bWillWatch = true; + m_bWatching = true; + + m_bNotified = true; + m_cvWatchdogCondition.notify_all(); +} + +void CWatchdog::endWatching() { + m_bWatching = false; + m_bWillWatch = false; + + m_bNotified = true; + m_cvWatchdogCondition.notify_all(); +} \ No newline at end of file diff --git a/src/helpers/Watchdog.hpp b/src/helpers/Watchdog.hpp new file mode 100644 index 00000000..1868bd0f --- /dev/null +++ b/src/helpers/Watchdog.hpp @@ -0,0 +1,30 @@ +#pragma once + +#include +#include +#include +#include + +class CWatchdog { + public: + // must be called from the main thread + CWatchdog(); + + void startWatching(); + void endWatching(); + + private: + std::chrono::high_resolution_clock::time_point m_tTriggered; + + pthread_t m_iMainThreadPID = 0; + + bool m_bWatching = false; + bool m_bWillWatch = false; + + std::unique_ptr m_pWatchdog; + std::mutex m_mWatchdogMutex; + bool m_bNotified = false; + std::condition_variable m_cvWatchdogCondition; +}; + +inline std::unique_ptr g_pWatchdog; \ No newline at end of file -- cgit v1.2.3