QTimerInfoList: change timerWait() to return std::optional<ms>
QEventDispatcherGlib: it used the out-parameter timespec to set a timeout in milliseconds, making timerWait() return milliseconds is more straightforward. Also timerWait() returns the time rounded to milliseconds, so the code in QEventDispatcherGlib that rounded up the timespec::tv_nsec part was no-op, tv_nsec was always 0 IIUC. In QEventDispatcherGlib, guard against overflow with qt_saturate. Change-Id: Ie6f78374d00cbe8a6adf7b50ed67c8c86ab4d29d Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
parent
99c6190bdf
commit
3ca6e7e6c7
@ -555,23 +555,25 @@ int QEventDispatcherCoreFoundation::remainingTime(int timerId)
|
||||
return m_timerInfoList.timerRemainingTime(timerId);
|
||||
}
|
||||
|
||||
static double timespecToSeconds(const timespec &spec)
|
||||
{
|
||||
static double nanosecondsPerSecond = 1.0 * 1000 * 1000 * 1000;
|
||||
return spec.tv_sec + (spec.tv_nsec / nanosecondsPerSecond);
|
||||
}
|
||||
|
||||
void QEventDispatcherCoreFoundation::updateTimers()
|
||||
{
|
||||
if (m_timerInfoList.size() > 0) {
|
||||
// We have Qt timers registered, so create or reschedule CF timer to match
|
||||
|
||||
timespec tv = { -1, -1 };
|
||||
CFAbsoluteTime timeToFire = m_timerInfoList.timerWait(tv) ?
|
||||
using namespace std::chrono_literals;
|
||||
using DoubleSeconds = std::chrono::duration<double, std::ratio<1>>;
|
||||
|
||||
CFAbsoluteTime timeToFire;
|
||||
auto opt = m_timerInfoList.timerWait();
|
||||
DoubleSeconds secs{};
|
||||
if (opt) {
|
||||
// We have a timer ready to fire right now, or some time in the future
|
||||
CFAbsoluteTimeGetCurrent() + timespecToSeconds(tv)
|
||||
secs = DoubleSeconds{*opt};
|
||||
timeToFire = CFAbsoluteTimeGetCurrent() + secs.count();
|
||||
} else {
|
||||
// We have timers, but they are all currently blocked by callbacks
|
||||
: kCFTimeIntervalDistantFuture;
|
||||
timeToFire = kCFTimeIntervalDistantFuture;
|
||||
}
|
||||
|
||||
if (!m_runLoopTimer) {
|
||||
m_runLoopTimer = CFRunLoopTimerCreateWithHandler(kCFAllocatorDefault,
|
||||
@ -587,9 +589,9 @@ void QEventDispatcherCoreFoundation::updateTimers()
|
||||
qCDebug(lcEventDispatcherTimers) << "Re-scheduled CFRunLoopTimer" << m_runLoopTimer;
|
||||
}
|
||||
|
||||
m_overdueTimerScheduled = !timespecToSeconds(tv);
|
||||
m_overdueTimerScheduled = secs > 0s;
|
||||
|
||||
qCDebug(lcEventDispatcherTimers) << "Next timeout in" << tv << "seconds";
|
||||
qCDebug(lcEventDispatcherTimers) << "Next timeout in" << secs;
|
||||
|
||||
} else {
|
||||
// No Qt timers are registered, so make sure we're not running any CF timers
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include "qeventdispatcher_glib_p.h"
|
||||
#include "qeventdispatcher_unix_p.h"
|
||||
|
||||
#include <private/qnumeric_p.h>
|
||||
#include <private/qthread_p.h>
|
||||
|
||||
#include "qcoreapplication.h"
|
||||
@ -14,6 +15,8 @@
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
struct GPollFDWithQSocketNotifier
|
||||
@ -95,11 +98,13 @@ struct GTimerSource
|
||||
|
||||
static gboolean timerSourcePrepareHelper(GTimerSource *src, gint *timeout)
|
||||
{
|
||||
timespec tv = { 0l, 0l };
|
||||
if (!(src->processEventsFlags & QEventLoop::X11ExcludeTimers) && src->timerList.timerWait(tv))
|
||||
*timeout = (tv.tv_sec * 1000) + ((tv.tv_nsec + 999999) / 1000 / 1000);
|
||||
else
|
||||
if (src->processEventsFlags & QEventLoop::X11ExcludeTimers) {
|
||||
*timeout = -1;
|
||||
return true;
|
||||
}
|
||||
|
||||
auto msecs = src->timerList.timerWait().value_or(-1ms);
|
||||
*timeout = qt_saturate<gint>(msecs.count());
|
||||
|
||||
return (*timeout == 0);
|
||||
}
|
||||
|
@ -27,6 +27,8 @@
|
||||
# include <pipeDrv.h>
|
||||
#endif
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
static const char *socketType(QSocketNotifier::Type type)
|
||||
@ -427,8 +429,15 @@ bool QEventDispatcherUNIX::processEvents(QEventLoop::ProcessEventsFlags flags)
|
||||
timespec *tm = nullptr;
|
||||
timespec wait_tm = { 0, 0 };
|
||||
|
||||
if (!canWait || (include_timers && d->timerList.timerWait(wait_tm)))
|
||||
if (!canWait) {
|
||||
tm = &wait_tm;
|
||||
} else if (include_timers) {
|
||||
std::optional<std::chrono::milliseconds> msecs = d->timerList.timerWait();
|
||||
if (msecs) {
|
||||
wait_tm = durationToTimespec(*msecs);
|
||||
tm = &wait_tm;
|
||||
}
|
||||
}
|
||||
|
||||
d->pollfds.clear();
|
||||
d->pollfds.reserve(1 + (include_notifiers ? d->socketNotifiers.size() : 0));
|
||||
|
@ -583,19 +583,15 @@ void QEventDispatcherWasm::updateNativeTimer()
|
||||
// access to m_timerInfo), and then call native API to set the new
|
||||
// wakeup time on the main thread.
|
||||
|
||||
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);
|
||||
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]() {
|
||||
const std::optional<std::chrono::milliseconds> wait = m_timerInfo->timerWait();
|
||||
const auto toWaitDuration = 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);
|
||||
auto maintainNativeTimer = [this, wait, toWaitDuration, newTargetTime]() {
|
||||
Q_ASSERT(emscripten_is_main_runtime_thread());
|
||||
|
||||
if (!hasTimer) {
|
||||
if (!wait) {
|
||||
if (m_timerId > 0) {
|
||||
emscripten_clear_timeout(m_timerId);
|
||||
m_timerId = 0;
|
||||
|
@ -230,7 +230,11 @@ static void calculateNextTimeout(QTimerInfo *t, steady_clock::time_point now)
|
||||
}
|
||||
}
|
||||
|
||||
bool QTimerInfoList::timerWait(timespec &tm)
|
||||
/*
|
||||
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()
|
||||
{
|
||||
steady_clock::time_point now = updateCurrentTime();
|
||||
|
||||
@ -238,15 +242,12 @@ bool QTimerInfoList::timerWait(timespec &tm)
|
||||
// Find first waiting timer not already active
|
||||
auto it = std::find_if(timers.cbegin(), timers.cend(), isWaiting);
|
||||
if (it == timers.cend())
|
||||
return false;
|
||||
return std::nullopt;
|
||||
|
||||
nanoseconds timeToWait = (*it)->timeout - now;
|
||||
if (timeToWait > 0ns)
|
||||
tm = durationToTimespec(roundToMillisecond(timeToWait));
|
||||
else
|
||||
tm = {0, 0};
|
||||
|
||||
return true;
|
||||
return roundToMillisecond(timeToWait);
|
||||
return 0ms;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -46,7 +46,7 @@ public:
|
||||
|
||||
std::chrono::steady_clock::time_point currentTime;
|
||||
|
||||
bool timerWait(timespec &);
|
||||
std::optional<std::chrono::milliseconds> timerWait();
|
||||
void timerInsert(QTimerInfo *);
|
||||
|
||||
qint64 timerRemainingTime(int timerId);
|
||||
|
@ -115,6 +115,7 @@ void QCocoaEventDispatcherPrivate::maybeStartCFRunLoopTimer()
|
||||
return;
|
||||
}
|
||||
|
||||
using DoubleSeconds = std::chrono::duration<double, std::ratio<1>>;
|
||||
if (!runLoopTimerRef) {
|
||||
// start the CFRunLoopTimer
|
||||
CFAbsoluteTime ttf = CFAbsoluteTimeGetCurrent();
|
||||
@ -122,10 +123,10 @@ void QCocoaEventDispatcherPrivate::maybeStartCFRunLoopTimer()
|
||||
CFTimeInterval oneyear = CFTimeInterval(3600. * 24. * 365.);
|
||||
|
||||
// Q: when should the CFRunLoopTimer fire for the first time?
|
||||
struct timespec tv;
|
||||
if (timerInfoList.timerWait(tv)) {
|
||||
if (auto opt = timerInfoList.timerWait()) {
|
||||
// A: when we have timers to fire, of course
|
||||
interval = qMax(tv.tv_sec + tv.tv_nsec / 1000000000., 0.0000001);
|
||||
DoubleSeconds secs{*opt};
|
||||
interval = qMax(secs.count(), 0.0000001);
|
||||
} else {
|
||||
// this shouldn't really happen, but in case it does, set the timer to fire a some point in the distant future
|
||||
interval = oneyear;
|
||||
@ -145,10 +146,10 @@ void QCocoaEventDispatcherPrivate::maybeStartCFRunLoopTimer()
|
||||
CFTimeInterval interval;
|
||||
|
||||
// Q: when should the timer first next?
|
||||
struct timespec tv;
|
||||
if (timerInfoList.timerWait(tv)) {
|
||||
if (auto opt = timerInfoList.timerWait()) {
|
||||
// A: when we have timers to fire, of course
|
||||
interval = qMax(tv.tv_sec + tv.tv_nsec / 1000000000., 0.0000001);
|
||||
DoubleSeconds secs{*opt};
|
||||
interval = qMax(secs.count(), 0.0000001);
|
||||
} else {
|
||||
// no timers can fire, but we cannot stop the CFRunLoopTimer, set the timer to fire at some
|
||||
// point in the distant future (the timer interval is one year)
|
||||
|
Loading…
x
Reference in New Issue
Block a user