QEventDispatcherWin32: properly support 64-bit timeouts
The Windows timer APIs take intervals as uint milliseconds. As a result, the code was unconditionally truncating an qint64 millisecond value to uint, leading to incorrect timeouts. Detect such cases, and implement a logic to adjust the interval passed to the Windows APIs and send a timer event at every second timeout. This will give a 1ms or 2ms error in the timeout calculation, but that should be an acceptable tolerance when we talk about intervals around 50 days or longer. Adjust the tst_QChronoTimer::remainingTimeInitial() test to check that the remaining time is not less than "expected interval - 1 minute". This should allow to catch cases when internal calculations are terribly broken. It's not really possible to provide even a manual test for timeouts that are larger than std::numeric_limits<uint>::max(), so just rely on the existing tests to make sure that the updated implementation does not break regular cases with smaller timeouts. Amends 0d0b346322f6b078e6fe60cd3612e8d08a0d5f00. Task-number: QTBUG-129170 Change-Id: Ib9885dcb7aca18e9fb193fad14bace0efeb437da Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
parent
34f1b6b6d1
commit
bb716c0111
@ -15,6 +15,8 @@
|
||||
#include "qcoreapplication_p.h"
|
||||
#include <private/qthread_p.h>
|
||||
|
||||
#include "q26numeric.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
#ifndef TIME_KILL_SYNCHRONOUS
|
||||
@ -298,7 +300,7 @@ static HWND qt_create_internal_window(const QEventDispatcherWin32 *eventDispatch
|
||||
|
||||
static ULONG calculateNextTimeout(WinTimerInfo *t, quint64 currentTime)
|
||||
{
|
||||
uint interval = t->interval;
|
||||
qint64 interval = t->interval;
|
||||
ULONG tolerance = TIMERV_DEFAULT_COALESCING;
|
||||
switch (t->timerType) {
|
||||
case Qt::PreciseTimer:
|
||||
@ -347,7 +349,13 @@ void QEventDispatcherWin32Private::registerTimer(WinTimerInfo *t)
|
||||
|
||||
bool ok = false;
|
||||
ULONG tolerance = calculateNextTimeout(t, qt_msectime());
|
||||
uint interval = t->interval;
|
||||
uint interval = q26::saturate_cast<uint>(t->interval);
|
||||
if (interval != t->interval) {
|
||||
// Now we'll need to post the timer event at each second timeout.
|
||||
interval = q26::saturate_cast<uint>(t->interval / 2 + 1);
|
||||
t->usesExtendedInterval = true;
|
||||
t->isSecondTimeout = false;
|
||||
}
|
||||
if (interval == 0u) {
|
||||
// optimization for single-shot-zero-timer
|
||||
QCoreApplication::postEvent(q, new QZeroTimerEvent(t->timerId));
|
||||
@ -390,6 +398,12 @@ void QEventDispatcherWin32Private::sendTimerEvent(int timerId)
|
||||
{
|
||||
WinTimerInfo *t = timerDict.value(timerId);
|
||||
if (t && !t->inTimerEvent) {
|
||||
if (t->usesExtendedInterval && !t->isSecondTimeout) {
|
||||
// That's the case when the interval is larger than uint
|
||||
t->isSecondTimeout = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// send event, but don't allow it to recurse
|
||||
t->inTimerEvent = true;
|
||||
|
||||
@ -403,6 +417,7 @@ void QEventDispatcherWin32Private::sendTimerEvent(int timerId)
|
||||
if (t->timerId == -1) {
|
||||
delete t;
|
||||
} else {
|
||||
t->isSecondTimeout = false;
|
||||
t->inTimerEvent = false;
|
||||
}
|
||||
}
|
||||
@ -702,6 +717,8 @@ void QEventDispatcherWin32::registerTimer(Qt::TimerId timerId, Duration interval
|
||||
t->obj = object;
|
||||
t->inTimerEvent = false;
|
||||
t->fastTimerId = 0;
|
||||
t->usesExtendedInterval = false;
|
||||
t->isSecondTimeout = false;
|
||||
|
||||
d->registerTimer(t);
|
||||
|
||||
|
@ -93,6 +93,8 @@ struct WinTimerInfo { // internal timer info
|
||||
Qt::TimerType timerType;
|
||||
UINT fastTimerId;
|
||||
bool inTimerEvent;
|
||||
bool usesExtendedInterval;
|
||||
bool isSecondTimeout;
|
||||
};
|
||||
|
||||
class QZeroTimerEvent : public QTimerEvent
|
||||
|
@ -284,6 +284,14 @@ void tst_QChronoTimer::remainingTimeInitial()
|
||||
} else {
|
||||
QCOMPARE_LE(rt, startTimeNs);
|
||||
}
|
||||
|
||||
if (startTimeNs > 1min) {
|
||||
// The test will surely take less than 1 minute
|
||||
auto startMinusOneMinute = std::chrono::nanoseconds{startTimeNs - 1min};
|
||||
// If the remaining time is less than startMinusOneMinute, then
|
||||
// something is terribly wrong with the internal interval calculations
|
||||
QCOMPARE_GE(rt, startMinusOneMinute);
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QChronoTimer::remainingTimeDuringActivation_data()
|
||||
|
Loading…
x
Reference in New Issue
Block a user