diff --git a/src/corelib/kernel/qeventdispatcher_wasm.cpp b/src/corelib/kernel/qeventdispatcher_wasm.cpp index 6818a5eab8c..7727b02c65d 100644 --- a/src/corelib/kernel/qeventdispatcher_wasm.cpp +++ b/src/corelib/kernel/qeventdispatcher_wasm.cpp @@ -14,6 +14,8 @@ #include #include +using namespace std::chrono_literals; + QT_BEGIN_NAMESPACE // using namespace emscripten; @@ -507,15 +509,15 @@ void QEventDispatcherWasm::updateNativeTimer() // access to m_timerInfo), and then call native API to set the new // wakeup time on the main thread. - auto timespecToMsec = [](timespec ts) -> uint64_t { - return ts.tv_sec * 1000 + ts.tv_nsec / (1000 * 1000); + using namespace std::chrono; + auto timespecToMsec = [](timespec ts) -> milliseconds { + return duration_cast(seconds{ts.tv_sec} + nanoseconds{ts.tv_nsec}); }; timespec toWait; bool hasTimer = m_timerInfo->timerWait(toWait); - uint64_t currentTime = timespecToMsec(m_timerInfo->currentTime); - uint64_t toWaitDuration = timespecToMsec(toWait); - uint64_t newTargetTime = currentTime + toWaitDuration; - + const milliseconds toWaitDuration = timespecToMsec(toWait); + const time_point newTargetTimePoint = m_timerInfo->currentTime + toWaitDuration; + auto newTargetTime = duration_cast(newTargetTimePoint.time_since_epoch()); auto maintainNativeTimer = [this, hasTimer, toWaitDuration, newTargetTime]() { Q_ASSERT(emscripten_is_main_runtime_thread()); @@ -523,18 +525,20 @@ void QEventDispatcherWasm::updateNativeTimer() if (m_timerId > 0) { emscripten_clear_timeout(m_timerId); m_timerId = 0; - m_timerTargetTime = 0; + m_timerTargetTime = 0ms; } return; } - if (m_timerTargetTime != 0 && newTargetTime >= m_timerTargetTime) + if (m_timerTargetTime != 0ms && newTargetTime >= m_timerTargetTime) return; // existing timer is good qCDebug(lcEventDispatcherTimers) - << "Created new native timer with wait" << toWaitDuration << "timeout" << newTargetTime; + << "Created new native timer with wait" << toWaitDuration.count() << "ms" + << "timeout" << newTargetTime.count() << "ms"; emscripten_clear_timeout(m_timerId); - m_timerId = emscripten_set_timeout(&QEventDispatcherWasm::callProcessTimers, toWaitDuration, this); + m_timerId = emscripten_set_timeout(&QEventDispatcherWasm::callProcessTimers, + toWaitDuration.count(), this); m_timerTargetTime = newTargetTime; }; @@ -565,7 +569,7 @@ void QEventDispatcherWasm::callProcessTimers(void *context) // Process timers on this thread if this is the main event dispatcher if (reinterpret_cast(context) == g_mainThreadEventDispatcher) { - g_mainThreadEventDispatcher->m_timerTargetTime = 0; + g_mainThreadEventDispatcher->m_timerTargetTime = 0ms; g_mainThreadEventDispatcher->processTimers(); return; } @@ -575,7 +579,7 @@ void QEventDispatcherWasm::callProcessTimers(void *context) std::lock_guard lock(g_staticDataMutex); if (g_secondaryThreadEventDispatchers.contains(context)) { QEventDispatcherWasm *eventDispatcher = reinterpret_cast(context); - eventDispatcher->m_timerTargetTime = 0; + eventDispatcher->m_timerTargetTime = 0ms; eventDispatcher->m_processTimers = true; eventDispatcher->wakeUp(); } diff --git a/src/corelib/kernel/qeventdispatcher_wasm_p.h b/src/corelib/kernel/qeventdispatcher_wasm_p.h index f253a1a95f6..d42bc6281ca 100644 --- a/src/corelib/kernel/qeventdispatcher_wasm_p.h +++ b/src/corelib/kernel/qeventdispatcher_wasm_p.h @@ -20,6 +20,7 @@ #include #include +#include #include #include #include @@ -99,7 +100,7 @@ private: QTimerInfoList *m_timerInfo = new QTimerInfoList(); long m_timerId = 0; - uint64_t m_timerTargetTime = 0; + std::chrono::milliseconds m_timerTargetTime{}; #if QT_CONFIG(thread) std::mutex m_mutex; diff --git a/src/corelib/kernel/qtimerinfo_unix.cpp b/src/corelib/kernel/qtimerinfo_unix.cpp index 01238d8d0e3..3be88c66eff 100644 --- a/src/corelib/kernel/qtimerinfo_unix.cpp +++ b/src/corelib/kernel/qtimerinfo_unix.cpp @@ -18,6 +18,8 @@ #include using namespace std::chrono; +// Implied by "using namespace std::chrono", but be explicit about it, for grep-ability +using namespace std::chrono_literals; QT_BEGIN_NAMESPACE @@ -33,9 +35,10 @@ QTimerInfoList::QTimerInfoList() firstTimerInfo = nullptr; } -timespec QTimerInfoList::updateCurrentTime() +steady_clock::time_point QTimerInfoList::updateCurrentTime() { - return (currentTime = qt_gettime()); + currentTime = steady_clock::now(); + return currentTime; } /* @@ -52,22 +55,21 @@ void QTimerInfoList::timerInsert(QTimerInfo *ti) insert(index+1, ti); } -static constexpr timespec roundToMillisecond(timespec val) +static constexpr milliseconds roundToMillisecond(nanoseconds val) { // always round up // worst case scenario is that the first trigger of a 1-ms timer is 0.999 ms late - - int ns = val.tv_nsec % (1000 * 1000); - if (ns) - val.tv_nsec += 1000 * 1000 - ns; - return normalizedTimespec(val); + return ceil(val); } -static_assert(roundToMillisecond({0, 0}) == timespec{0, 0}); -static_assert(roundToMillisecond({0, 1}) == timespec{0, 1'000'000}); -static_assert(roundToMillisecond({0, 999'999}) == timespec{0, 1'000'000}); -static_assert(roundToMillisecond({0, 1'000'000}) == timespec{0, 1'000'000}); -static_assert(roundToMillisecond({0, 999'999'999}) == timespec{1, 0}); -static_assert(roundToMillisecond({1, 0}) == timespec{1, 0}); + +static_assert(roundToMillisecond(0ns) == 0ms); +static_assert(roundToMillisecond(1ns) == 1ms); +static_assert(roundToMillisecond(999'999ns) == 1ms); +static_assert(roundToMillisecond(1'000'000ns) == 1ms); +static_assert(roundToMillisecond(999'000'000ns) == 999ms); +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) { @@ -104,7 +106,7 @@ QDebug operator<<(QDebug s, Qt::TimerType t) } #endif -static void calculateCoarseTimerTimeout(QTimerInfo *t, timespec now) +static void calculateCoarseTimerTimeout(QTimerInfo *t, steady_clock::time_point now) { // The coarse timer works like this: // - interval under 40 ms: round to even @@ -124,14 +126,10 @@ static void calculateCoarseTimerTimeout(QTimerInfo *t, timespec now) Q_ASSERT(t->interval >= 20ms); - auto recalculate = [&](const milliseconds fracMsec) { - if (fracMsec == 1000ms) { - ++t->timeout.tv_sec; - t->timeout.tv_nsec = 0; - } else { - t->timeout.tv_nsec = nanoseconds{fracMsec}.count(); - } + const auto timeoutInSecs = time_point_cast(t->timeout); + auto recalculate = [&](const milliseconds frac) { + t->timeout = timeoutInSecs + frac; if (t->timeout < now) t->timeout += t->interval; }; @@ -139,7 +137,7 @@ static void calculateCoarseTimerTimeout(QTimerInfo *t, timespec now) // Calculate how much we can round and still keep within 5% error const milliseconds absMaxRounding = t->interval / 20; - auto fracMsec = duration_cast(nanoseconds{t->timeout.tv_nsec}); + auto fracMsec = duration_cast(t->timeout - timeoutInSecs); if (t->interval < 100ms && t->interval != 25ms && t->interval != 50ms && t->interval != 75ms) { auto fracCount = fracMsec.count(); @@ -222,7 +220,7 @@ static void calculateCoarseTimerTimeout(QTimerInfo *t, timespec now) recalculate(fracMsec); } -static void calculateNextTimeout(QTimerInfo *t, timespec now) +static void calculateNextTimeout(QTimerInfo *t, steady_clock::time_point now) { switch (t->timerType) { case Qt::PreciseTimer: @@ -245,10 +243,9 @@ static void calculateNextTimeout(QTimerInfo *t, timespec now) case Qt::VeryCoarseTimer: // t->interval already rounded to full seconds in registerTimer() - const auto secs = duration_cast(t->interval).count(); - t->timeout.tv_sec += secs; - if (t->timeout.tv_sec <= now.tv_sec) - t->timeout.tv_sec = now.tv_sec + secs; + t->timeout += t->interval; + if (t->timeout <= now) + t->timeout = time_point_cast(now + t->interval); #ifdef QTIMERINFO_DEBUG t->expected.tv_sec += t->interval; if (t->expected.tv_sec <= currentTime.tv_sec) @@ -265,13 +262,9 @@ static void calculateNextTimeout(QTimerInfo *t, timespec now) #endif } -/* - Returns the time to wait for the next timer, or null if no timers - are waiting. -*/ bool QTimerInfoList::timerWait(timespec &tm) { - timespec now = updateCurrentTime(); + steady_clock::time_point now = updateCurrentTime(); auto isWaiting = [](QTimerInfo *tinfo) { return !tinfo->activateRef; }; // Find first waiting timer not already active @@ -280,9 +273,10 @@ bool QTimerInfoList::timerWait(timespec &tm) return false; QTimerInfo *t = *it; - if (now < t->timeout) // Time to wait - tm = roundToMillisecond(t->timeout - now); - else // No time to wait + nanoseconds timeToWait = t->timeout - now; + if (timeToWait > 0ns) + tm = durationToTimespec(roundToMillisecond(timeToWait)); + else tm = {0, 0}; return true; @@ -300,21 +294,20 @@ qint64 QTimerInfoList::timerRemainingTime(int timerId) milliseconds QTimerInfoList::remainingDuration(int timerId) { - timespec now = updateCurrentTime(); + const steady_clock::time_point now = updateCurrentTime(); auto it = findTimerById(timerId); if (it == cend()) { #ifndef QT_NO_DEBUG qWarning("QTimerInfoList::timerRemainingTime: timer id %i not found", timerId); #endif - return milliseconds{-1}; + return -1ms; } const QTimerInfo *t = *it; if (now < t->timeout) // time to wait - return timespecToChronoMs(roundToMillisecond(t->timeout - now)); - else - return milliseconds{0}; + return roundToMillisecond(t->timeout - now); + return 0ms; } void QTimerInfoList::registerTimer(int timerId, qint64 interval, Qt::TimerType timerType, QObject *object) @@ -332,7 +325,7 @@ void QTimerInfoList::registerTimer(int timerId, milliseconds interval, t->obj = object; t->activateRef = nullptr; - timespec expected = updateCurrentTime() + interval; + steady_clock::time_point expected = updateCurrentTime() + interval; switch (timerType) { case Qt::PreciseTimer: @@ -360,14 +353,12 @@ void QTimerInfoList::registerTimer(int timerId, milliseconds interval, } Q_FALLTHROUGH(); case Qt::VeryCoarseTimer: - const seconds secs = roundToSecs(t->interval); - t->interval = secs; - t->timeout.tv_sec = currentTime.tv_sec + secs.count(); - t->timeout.tv_nsec = 0; - - // if we're past the half-second mark, increase the timeout again - if (currentTime.tv_nsec > nanoseconds{500ms}.count()) - ++t->timeout.tv_sec; + t->interval = roundToSecs(t->interval); + const auto currentTimeInSecs = floor(currentTime); + t->timeout = currentTimeInSecs + t->interval; + // If we're past the half-second mark, increase the timeout again + if (currentTime - currentTimeInSecs > 500ms) + t->timeout += 1s; } timerInsert(t); @@ -440,7 +431,7 @@ int QTimerInfoList::activateTimers() firstTimerInfo = nullptr; - timespec now = updateCurrentTime(); + const steady_clock::time_point now = updateCurrentTime(); // qDebug() << "Thread" << QThread::currentThreadId() << "woken up at" << now; // Find out how many timer have expired auto stillActive = [&now](const QTimerInfo *t) { return now < t->timeout; }; diff --git a/src/corelib/kernel/qtimerinfo_unix_p.h b/src/corelib/kernel/qtimerinfo_unix_p.h index fe3257b57b8..70d5dd85fbd 100644 --- a/src/corelib/kernel/qtimerinfo_unix_p.h +++ b/src/corelib/kernel/qtimerinfo_unix_p.h @@ -24,6 +24,7 @@ #include "qabstracteventdispatcher.h" #include // struct timeval +#include QT_BEGIN_NAMESPACE @@ -32,7 +33,7 @@ struct QTimerInfo { int id; // - timer identifier Qt::TimerType timerType; // - timer type std::chrono::milliseconds interval; // - timer interval - timespec timeout; // - when to actually fire + std::chrono::steady_clock::time_point timeout; // - when to actually fire QObject *obj; // - object to receive event QTimerInfo **activateRef; // - ref from activateTimers @@ -51,8 +52,8 @@ class Q_CORE_EXPORT QTimerInfoList : public QList public: QTimerInfoList(); - timespec currentTime; - timespec updateCurrentTime(); + std::chrono::steady_clock::time_point currentTime; + std::chrono::steady_clock::time_point updateCurrentTime(); bool timerWait(timespec &); void timerInsert(QTimerInfo *);