From 9555b201fe47b4a74a432d3cb4fe7dba20cf894c Mon Sep 17 00:00:00 2001 From: outfoxxed Date: Wed, 31 Jul 2024 02:37:05 -0700 Subject: [PATCH] core/clock: fix instability causing timer to fire multiple times If the signal was fired slightly before the scheduled time, it would schedule itself again a couple ms in the future. --- src/core/clock.cpp | 80 +++++++++++++++++++++++++++++----------------- src/core/clock.hpp | 8 ++++- 2 files changed, 58 insertions(+), 30 deletions(-) diff --git a/src/core/clock.cpp b/src/core/clock.cpp index 0af9762e..ee396ac8 100644 --- a/src/core/clock.cpp +++ b/src/core/clock.cpp @@ -8,7 +8,7 @@ #include "util.hpp" SystemClock::SystemClock(QObject* parent): QObject(parent) { - QObject::connect(&this->timer, &QTimer::timeout, this, &SystemClock::update); + QObject::connect(&this->timer, &QTimer::timeout, this, &SystemClock::onTimeout); this->update(); } @@ -30,41 +30,63 @@ void SystemClock::setPrecision(SystemClock::Enum precision) { this->update(); } +void SystemClock::onTimeout() { + this->setTime(this->nextTime); + this->schedule(this->nextTime); +} + void SystemClock::update() { - auto time = QTime::currentTime(); - if (this->mEnabled) { - auto secondPrecision = this->mPrecision >= SystemClock::Seconds; - auto secondChanged = this->setSeconds(secondPrecision ? time.second() : 0); - - auto minutePrecision = this->mPrecision >= SystemClock::Minutes; - auto minuteChanged = this->setMinutes(minutePrecision ? time.minute() : 0); - - auto hourPrecision = this->mPrecision >= SystemClock::Hours; - auto hourChanged = this->setHours(hourPrecision ? time.hour() : 0); - - DropEmitter::call(secondChanged, minuteChanged, hourChanged); - - auto nextTime = QTime( - hourPrecision ? time.hour() : 0, - minutePrecision ? time.minute() : 0, - secondPrecision ? time.second() : 0 - ); - - if (secondPrecision) nextTime = nextTime.addSecs(1); - else if (minutePrecision) nextTime = nextTime.addSecs(60); - else if (hourPrecision) nextTime = nextTime.addSecs(3600); - - auto delay = time.msecsTo(nextTime); - // day rollover - if (delay < 0) delay += 86400000; - - this->timer.start(delay); + this->setTime(QTime::currentTime()); + this->schedule(QTime::currentTime()); } else { this->timer.stop(); } } +void SystemClock::setTime(QTime time) { + auto secondPrecision = this->mPrecision >= SystemClock::Seconds; + auto secondChanged = this->setSeconds(secondPrecision ? time.second() : 0); + + auto minutePrecision = this->mPrecision >= SystemClock::Minutes; + auto minuteChanged = this->setMinutes(minutePrecision ? time.minute() : 0); + + auto hourPrecision = this->mPrecision >= SystemClock::Hours; + auto hourChanged = this->setHours(hourPrecision ? time.hour() : 0); + + DropEmitter::call(secondChanged, minuteChanged, hourChanged); +} + +void SystemClock::schedule(QTime floor) { + auto secondPrecision = this->mPrecision >= SystemClock::Seconds; + auto minutePrecision = this->mPrecision >= SystemClock::Minutes; + auto hourPrecision = this->mPrecision >= SystemClock::Hours; + +setnext: + auto nextTime = QTime( + hourPrecision ? floor.hour() : 0, + minutePrecision ? floor.minute() : 0, + secondPrecision ? floor.second() : 0 + ); + + if (secondPrecision) nextTime = nextTime.addSecs(1); + else if (minutePrecision) nextTime = nextTime.addSecs(60); + else if (hourPrecision) nextTime = nextTime.addSecs(3600); + + auto delay = QTime::currentTime().msecsTo(nextTime); + + // If off by more than 2 hours we likely wrapped around midnight. + if (delay < -7200000) delay += 86400000; + else if (delay < 0) { + // Otherwise its just the timer being unstable. + floor = QTime::currentTime(); + goto setnext; + } + + this->timer.start(delay); + this->nextTime = nextTime; +} + DEFINE_MEMBER_GETSET(SystemClock, hours, setHours); DEFINE_MEMBER_GETSET(SystemClock, minutes, setMinutes); DEFINE_MEMBER_GETSET(SystemClock, seconds, setSeconds); diff --git a/src/core/clock.hpp b/src/core/clock.hpp index b3ac1228..7f0dbf74 100644 --- a/src/core/clock.hpp +++ b/src/core/clock.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include @@ -49,7 +50,7 @@ signals: void secondsChanged(); private slots: - void update(); + void onTimeout(); private: bool mEnabled = true; @@ -58,6 +59,11 @@ private: quint32 mMinutes = 0; quint32 mSeconds = 0; QTimer timer; + QTime nextTime; + + void update(); + void setTime(QTime time); + void schedule(QTime floor); DECLARE_PRIVATE_MEMBER(SystemClock, hours, setHours, mHours, hoursChanged); DECLARE_PRIVATE_MEMBER(SystemClock, minutes, setMinutes, mMinutes, minutesChanged);