QTimerInfoList/Unix: use chrono::steady_clock::time_point
I.e. use chrono first, see linked task for more details. Task-number: QTBUG-110059 Change-Id: Ie0908f9446957d4383fb7a70b67c16b78605a0f3 Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
parent
c0e0b56055
commit
be3dd0115b
@ -14,6 +14,8 @@
|
||||
#include <emscripten/threading.h>
|
||||
#include <emscripten/val.h>
|
||||
|
||||
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<milliseconds>(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<milliseconds>(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<QEventDispatcherWasm *>(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<std::mutex> lock(g_staticDataMutex);
|
||||
if (g_secondaryThreadEventDispatchers.contains(context)) {
|
||||
QEventDispatcherWasm *eventDispatcher = reinterpret_cast<QEventDispatcherWasm *>(context);
|
||||
eventDispatcher->m_timerTargetTime = 0;
|
||||
eventDispatcher->m_timerTargetTime = 0ms;
|
||||
eventDispatcher->m_processTimers = true;
|
||||
eventDispatcher->wakeUp();
|
||||
}
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include <QtCore/qloggingcategory.h>
|
||||
#include <QtCore/qwaitcondition.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <tuple>
|
||||
@ -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;
|
||||
|
@ -18,6 +18,8 @@
|
||||
#include <sys/times.h>
|
||||
|
||||
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<milliseconds>(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<seconds>(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<milliseconds>(nanoseconds{t->timeout.tv_nsec});
|
||||
auto fracMsec = duration_cast<milliseconds>(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<seconds>(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<seconds>(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<seconds>(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; };
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include "qabstracteventdispatcher.h"
|
||||
|
||||
#include <sys/time.h> // struct timeval
|
||||
#include <chrono>
|
||||
|
||||
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<QTimerInfo*>
|
||||
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 *);
|
||||
|
Loading…
x
Reference in New Issue
Block a user