QTimerInfo: store nanoseconds instead of milliseconds
No change in behavior, other than the ability for the precise timers to be even more precise. There's a reduction in the stored interval range from 292 million years to 292 years, but there's no change in behavior because the timeout is stored as a steady_clock::time_point, which is nanoseconds with libstdc++ and libc++. Now, if there's an overflow, it'll happen on the call to registerTimer() instead of inside the calculation of the wake-up. Change-Id: I83dda2d36c904517b3c0fffd17b3d1f2d9505c1c Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org> Reviewed-by: Ahmad Samir <a.samirh78@gmail.com>bb10
parent
1ca89b65d8
commit
9dc2935462
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
#include <glib.h>
|
||||
|
||||
using namespace std::chrono;
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
|
@ -102,8 +103,8 @@ static gboolean timerSourcePrepareHelper(GTimerSource *src, gint *timeout)
|
|||
return true;
|
||||
}
|
||||
|
||||
auto msecs = src->timerList.timerWait().value_or(-1ms);
|
||||
*timeout = qt_saturate<gint>(msecs.count());
|
||||
auto remaining = src->timerList.timerWait().value_or(-1ms);
|
||||
*timeout = qt_saturate<gint>(ceil<milliseconds>(remaining).count());
|
||||
|
||||
return (*timeout == 0);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ static constexpr bool UsingEventfd = false;
|
|||
# include <pipeDrv.h>
|
||||
#endif
|
||||
|
||||
using namespace std::chrono;
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
|
@ -431,8 +432,8 @@ bool QEventDispatcherUNIX::processEvents(QEventLoop::ProcessEventsFlags flags)
|
|||
QDeadlineTimer deadline;
|
||||
if (canWait) {
|
||||
if (include_timers) {
|
||||
std::optional<std::chrono::milliseconds> msecs = d->timerList.timerWait();
|
||||
deadline = msecs ? QDeadlineTimer{*msecs}
|
||||
std::optional<nanoseconds> remaining = d->timerList.timerWait();
|
||||
deadline = remaining ? QDeadlineTimer{*remaining}
|
||||
: QDeadlineTimer(QDeadlineTimer::Forever);
|
||||
} else {
|
||||
deadline = QDeadlineTimer(QDeadlineTimer::Forever);
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
#include <emscripten/threading.h>
|
||||
#include <emscripten/val.h>
|
||||
|
||||
using namespace std::chrono;
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
|
@ -619,8 +620,8 @@ void QEventDispatcherWasm::updateNativeTimer()
|
|||
// access to m_timerInfo), and then call native API to set the new
|
||||
// wakeup time on the main thread.
|
||||
|
||||
const std::optional<std::chrono::milliseconds> wait = m_timerInfo->timerWait();
|
||||
const auto toWaitDuration = wait.value_or(0ms);
|
||||
const std::optional<std::chrono::nanoseconds> wait = m_timerInfo->timerWait();
|
||||
const auto toWaitDuration = duration_cast<milliseconds>(wait.value_or(0ms));
|
||||
const auto newTargetTimePoint = m_timerInfo->currentTime + toWaitDuration;
|
||||
auto epochNsecs = newTargetTimePoint.time_since_epoch();
|
||||
auto newTargetTime = std::chrono::duration_cast<std::chrono::milliseconds>(epochNsecs);
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ static_assert(roundToMillisecond(999'000'001ns) == 1000ms);
|
|||
static_assert(roundToMillisecond(999'999'999ns) == 1000ms);
|
||||
static_assert(roundToMillisecond(1s) == 1s);
|
||||
|
||||
static constexpr seconds roundToSecs(milliseconds msecs)
|
||||
static constexpr seconds roundToSecs(nanoseconds interval)
|
||||
{
|
||||
// The very coarse timer is based on full second precision, so we want to
|
||||
// round the interval to the closest second, rounding 500ms up to 1s.
|
||||
|
|
@ -86,8 +86,8 @@ static constexpr seconds roundToSecs(milliseconds msecs)
|
|||
// 1500 2 2
|
||||
// 2500 2 3
|
||||
|
||||
auto secs = duration_cast<seconds>(msecs);
|
||||
const milliseconds frac = msecs - secs;
|
||||
auto secs = duration_cast<seconds>(interval);
|
||||
const nanoseconds frac = interval - secs;
|
||||
if (frac >= 500ms)
|
||||
++secs;
|
||||
return secs;
|
||||
|
|
@ -122,14 +122,15 @@ static void calculateCoarseTimerTimeout(QTimerInfo *t, steady_clock::time_point
|
|||
};
|
||||
|
||||
// Calculate how much we can round and still keep within 5% error
|
||||
const milliseconds absMaxRounding = t->interval / 20;
|
||||
milliseconds interval = roundToMillisecond(t->interval);
|
||||
const milliseconds absMaxRounding = interval / 20;
|
||||
|
||||
auto fracMsec = duration_cast<milliseconds>(t->timeout - timeoutInSecs);
|
||||
|
||||
if (t->interval < 100ms && t->interval != 25ms && t->interval != 50ms && t->interval != 75ms) {
|
||||
if (interval < 100ms && interval != 25ms && interval != 50ms && interval != 75ms) {
|
||||
auto fracCount = fracMsec.count();
|
||||
// special mode for timers of less than 100 ms
|
||||
if (t->interval < 50ms) {
|
||||
if (interval < 50ms) {
|
||||
// round to even
|
||||
// round towards multiples of 50 ms
|
||||
bool roundUp = (fracCount % 50) >= 25;
|
||||
|
|
@ -171,17 +172,17 @@ static void calculateCoarseTimerTimeout(QTimerInfo *t, steady_clock::time_point
|
|||
// towards a round-to-the-second
|
||||
// 3) if the interval is a multiple of 500 ms, we'll round towards the nearest
|
||||
// multiple of 500 ms
|
||||
if ((t->interval % 500) == 0ms) {
|
||||
if (t->interval >= 5s) {
|
||||
if ((interval % 500) == 0ms) {
|
||||
if (interval >= 5s) {
|
||||
fracMsec = fracMsec >= 500ms ? max : min;
|
||||
recalculate(fracMsec);
|
||||
return;
|
||||
} else {
|
||||
wantedBoundaryMultiple = 500ms;
|
||||
}
|
||||
} else if ((t->interval % 50) == 0ms) {
|
||||
} else if ((interval % 50) == 0ms) {
|
||||
// 4) same for multiples of 250, 200, 100, 50
|
||||
milliseconds mult50 = t->interval / 50;
|
||||
milliseconds mult50 = interval / 50;
|
||||
if ((mult50 % 4) == 0ms) {
|
||||
// multiple of 200
|
||||
wantedBoundaryMultiple = 200ms;
|
||||
|
|
@ -234,7 +235,7 @@ static void calculateNextTimeout(QTimerInfo *t, steady_clock::time_point now)
|
|||
Returns the time to wait for the first timer that has not been activated yet,
|
||||
otherwise returns std::nullopt.
|
||||
*/
|
||||
std::optional<std::chrono::milliseconds> QTimerInfoList::timerWait()
|
||||
std::optional<QTimerInfoList::Duration> QTimerInfoList::timerWait()
|
||||
{
|
||||
steady_clock::time_point now = updateCurrentTime();
|
||||
|
||||
|
|
@ -257,10 +258,10 @@ std::optional<std::chrono::milliseconds> QTimerInfoList::timerWait()
|
|||
*/
|
||||
qint64 QTimerInfoList::timerRemainingTime(int timerId)
|
||||
{
|
||||
return remainingDuration(timerId).count();
|
||||
return roundToMillisecond(remainingDuration(timerId)).count();
|
||||
}
|
||||
|
||||
milliseconds QTimerInfoList::remainingDuration(int timerId)
|
||||
QTimerInfoList::Duration QTimerInfoList::remainingDuration(int timerId)
|
||||
{
|
||||
const steady_clock::time_point now = updateCurrentTime();
|
||||
|
||||
|
|
@ -274,7 +275,7 @@ milliseconds QTimerInfoList::remainingDuration(int timerId)
|
|||
|
||||
const QTimerInfo *t = *it;
|
||||
if (now < t->timeout) // time to wait
|
||||
return roundToMillisecond(t->timeout - now);
|
||||
return t->timeout - now;
|
||||
return 0ms;
|
||||
}
|
||||
|
||||
|
|
@ -283,7 +284,7 @@ void QTimerInfoList::registerTimer(int timerId, qint64 interval, Qt::TimerType t
|
|||
registerTimer(timerId, milliseconds{interval}, timerType, object);
|
||||
}
|
||||
|
||||
void QTimerInfoList::registerTimer(int timerId, milliseconds interval,
|
||||
void QTimerInfoList::registerTimer(int timerId, Duration interval,
|
||||
Qt::TimerType timerType, QObject *object)
|
||||
{
|
||||
// correct the timer type first
|
||||
|
|
@ -299,7 +300,7 @@ void QTimerInfoList::registerTimer(int timerId, milliseconds interval,
|
|||
}
|
||||
|
||||
QTimerInfo *t = new QTimerInfo(timerId, interval, timerType, object);
|
||||
steady_clock::time_point expected = updateCurrentTime() + interval;
|
||||
QTimerInfo::TimePoint expected = updateCurrentTime() + interval;
|
||||
|
||||
switch (timerType) {
|
||||
case Qt::PreciseTimer:
|
||||
|
|
@ -310,6 +311,7 @@ void QTimerInfoList::registerTimer(int timerId, milliseconds interval,
|
|||
|
||||
case Qt::CoarseTimer:
|
||||
t->timeout = expected;
|
||||
t->interval = roundToMillisecond(interval);
|
||||
calculateCoarseTimerTimeout(t, currentTime);
|
||||
break;
|
||||
|
||||
|
|
|
|||
|
|
@ -25,14 +25,17 @@
|
|||
QT_BEGIN_NAMESPACE
|
||||
|
||||
// internal timer info
|
||||
struct QTimerInfo {
|
||||
QTimerInfo(int timerId, std::chrono::milliseconds msecs, Qt::TimerType type, QObject *obj)
|
||||
: interval(msecs), id(timerId), timerType(type), obj(obj)
|
||||
struct QTimerInfo
|
||||
{
|
||||
using Duration = QAbstractEventDispatcher::Duration;
|
||||
using TimePoint = std::chrono::time_point<std::chrono::steady_clock, Duration>;
|
||||
QTimerInfo(int timerId, Duration interval, Qt::TimerType type, QObject *obj)
|
||||
: interval(interval), id(timerId), timerType(type), obj(obj)
|
||||
{
|
||||
}
|
||||
|
||||
std::chrono::steady_clock::time_point timeout; // - when to actually fire
|
||||
std::chrono::milliseconds interval = std::chrono::milliseconds{-1}; // - timer interval
|
||||
TimePoint timeout = {}; // - when to actually fire
|
||||
Duration interval = Duration{-1}; // - timer interval
|
||||
int id = -1; // - timer identifier
|
||||
Qt::TimerType timerType; // - timer type
|
||||
QObject *obj = nullptr; // - object to receive event
|
||||
|
|
@ -42,18 +45,19 @@ struct QTimerInfo {
|
|||
class Q_CORE_EXPORT QTimerInfoList
|
||||
{
|
||||
public:
|
||||
using Duration = QAbstractEventDispatcher::Duration;
|
||||
QTimerInfoList();
|
||||
|
||||
std::chrono::steady_clock::time_point currentTime;
|
||||
|
||||
std::optional<std::chrono::milliseconds> timerWait();
|
||||
std::optional<Duration> timerWait();
|
||||
void timerInsert(QTimerInfo *);
|
||||
|
||||
qint64 timerRemainingTime(int timerId);
|
||||
std::chrono::milliseconds remainingDuration(int timerId);
|
||||
Duration remainingDuration(int timerId);
|
||||
|
||||
void registerTimer(int timerId, qint64 interval, Qt::TimerType timerType, QObject *object);
|
||||
void registerTimer(int timerId, std::chrono::milliseconds interval, Qt::TimerType timerType,
|
||||
void registerTimer(int timerId, Duration interval, Qt::TimerType timerType,
|
||||
QObject *object);
|
||||
bool unregisterTimer(int timerId);
|
||||
bool unregisterTimers(QObject *object);
|
||||
|
|
|
|||
Loading…
Reference in New Issue