eventloop: improve timer handling to avoid crashes

ref #11062
This commit is contained in:
Vaxry
2025-07-19 16:47:14 +02:00
parent b0b1c70e96
commit 77ab8962e3
3 changed files with 24 additions and 8 deletions

View File

@@ -102,25 +102,25 @@ void CEventLoopManager::enterLoop() {
void CEventLoopManager::onTimerFire() { void CEventLoopManager::onTimerFire() {
const auto CPY = m_timers.timers; const auto CPY = m_timers.timers;
for (auto const& t : CPY) { for (auto const& t : CPY) {
if (t.strongRef() > 1 /* if it's 1, it was lost. Don't call it. */ && t->passed() && !t->cancelled()) if (t.strongRef() > 2 /* if it's 2, it was lost. Don't call it. */ && t->passed() && !t->cancelled())
t->call(t); t->call(t);
} }
nudgeTimers(); scheduleRecalc();
} }
void CEventLoopManager::addTimer(SP<CEventLoopTimer> timer) { void CEventLoopManager::addTimer(SP<CEventLoopTimer> timer) {
if (std::ranges::contains(m_timers.timers, timer)) if (std::ranges::contains(m_timers.timers, timer))
return; return;
m_timers.timers.emplace_back(timer); m_timers.timers.emplace_back(timer);
nudgeTimers(); scheduleRecalc();
} }
void CEventLoopManager::removeTimer(SP<CEventLoopTimer> timer) { void CEventLoopManager::removeTimer(SP<CEventLoopTimer> timer) {
if (!std::ranges::contains(m_timers.timers, timer)) if (!std::ranges::contains(m_timers.timers, timer))
return; return;
std::erase_if(m_timers.timers, [timer](const auto& t) { return timer == t; }); std::erase_if(m_timers.timers, [timer](const auto& t) { return timer == t; });
nudgeTimers(); scheduleRecalc();
} }
static void timespecAddNs(timespec* pTimespec, int64_t delta) { static void timespecAddNs(timespec* pTimespec, int64_t delta) {
@@ -136,7 +136,21 @@ static void timespecAddNs(timespec* pTimespec, int64_t delta) {
} }
} }
void CEventLoopManager::scheduleRecalc() {
// do not do it instantly, do it later. Avoid recursive access to the timer
// vector, it could be catastrophic if we modify it while iterating
if (m_timers.recalcScheduled)
return;
m_timers.recalcScheduled = true;
doLater([this] { nudgeTimers(); });
}
void CEventLoopManager::nudgeTimers() { void CEventLoopManager::nudgeTimers() {
m_timers.recalcScheduled = false;
// remove timers that have gone missing // remove timers that have gone missing
std::erase_if(m_timers.timers, [](const auto& t) { return t.strongRef() <= 1; }); std::erase_if(m_timers.timers, [](const auto& t) { return t.strongRef() <= 1; });

View File

@@ -27,8 +27,8 @@ class CEventLoopManager {
void onTimerFire(); void onTimerFire();
// recalculates timers // schedules a recalc of the timers
void nudgeTimers(); void scheduleRecalc();
// schedules a function to run later, aka in a wayland idle event. // schedules a function to run later, aka in a wayland idle event.
void doLater(const std::function<void()>& fn); void doLater(const std::function<void()>& fn);
@@ -69,6 +69,7 @@ class CEventLoopManager {
private: private:
// Manages the event sources after AQ pollFDs change. // Manages the event sources after AQ pollFDs change.
void syncPollFDs(); void syncPollFDs();
void nudgeTimers();
struct SEventSourceData { struct SEventSourceData {
SP<Aquamarine::SPollFD> pollFD; SP<Aquamarine::SPollFD> pollFD;
@@ -84,6 +85,7 @@ class CEventLoopManager {
struct { struct {
std::vector<SP<CEventLoopTimer>> timers; std::vector<SP<CEventLoopTimer>> timers;
Hyprutils::OS::CFileDescriptor timerfd; Hyprutils::OS::CFileDescriptor timerfd;
bool recalcScheduled = false;
} m_timers; } m_timers;
SIdleData m_idle; SIdleData m_idle;

View File

@@ -14,13 +14,13 @@ CEventLoopTimer::CEventLoopTimer(std::optional<Time::steady_dur> timeout, std::f
void CEventLoopTimer::updateTimeout(std::optional<Time::steady_dur> timeout) { void CEventLoopTimer::updateTimeout(std::optional<Time::steady_dur> timeout) {
if (!timeout.has_value()) { if (!timeout.has_value()) {
m_expires.reset(); m_expires.reset();
g_pEventLoopManager->nudgeTimers(); g_pEventLoopManager->scheduleRecalc();
return; return;
} }
m_expires = Time::steadyNow() + *timeout; m_expires = Time::steadyNow() + *timeout;
g_pEventLoopManager->nudgeTimers(); g_pEventLoopManager->scheduleRecalc();
} }
bool CEventLoopTimer::passed() { bool CEventLoopTimer::passed() {