Fix the remainingTime() result after the first activation of a QTimer
On Windows, t->timeout was updated only once, at creation time, so the timer would always show as "overdue" after the first activation. The timer is updated to indicate the full remaining time during the slot activation, which is the behavior of the Unix and Glib dispatchers. Task-number: QTBUG-46940 Change-Id: I255870833a024a36adf6ffff13ecadb021c4358c Reviewed-by: Oswald Buddenhagen <oswald.buddenhagen@theqtcompany.com> Reviewed-by: Joerg Bornemann <joerg.bornemann@theqtcompany.com> Reviewed-by: Friedemann Kleint <Friedemann.Kleint@theqtcompany.com> Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
parent
1ac0c4a2f7
commit
d01d08971a
@ -561,6 +561,17 @@ static HWND qt_create_internal_window(const QEventDispatcherWin32 *eventDispatch
|
||||
return wnd;
|
||||
}
|
||||
|
||||
static void calculateNextTimeout(WinTimerInfo *t, quint64 currentTime)
|
||||
{
|
||||
uint interval = t->interval;
|
||||
if ((interval >= 20000u && t->timerType != Qt::PreciseTimer) || t->timerType == Qt::VeryCoarseTimer) {
|
||||
// round the interval, VeryCoarseTimers only have full second accuracy
|
||||
interval = ((interval + 500)) / 1000 * 1000;
|
||||
}
|
||||
t->interval = interval;
|
||||
t->timeout = currentTime + interval;
|
||||
}
|
||||
|
||||
void QEventDispatcherWin32Private::registerTimer(WinTimerInfo *t)
|
||||
{
|
||||
Q_ASSERT(internalHwnd);
|
||||
@ -568,6 +579,7 @@ void QEventDispatcherWin32Private::registerTimer(WinTimerInfo *t)
|
||||
Q_Q(QEventDispatcherWin32);
|
||||
|
||||
int ok = 0;
|
||||
calculateNextTimeout(t, qt_msectime());
|
||||
uint interval = t->interval;
|
||||
if (interval == 0u) {
|
||||
// optimization for single-shot-zero-timer
|
||||
@ -576,17 +588,13 @@ void QEventDispatcherWin32Private::registerTimer(WinTimerInfo *t)
|
||||
} else if ((interval < 20u || t->timerType == Qt::PreciseTimer) && qtimeSetEvent) {
|
||||
ok = t->fastTimerId = qtimeSetEvent(interval, 1, qt_fast_timer_proc, (DWORD_PTR)t,
|
||||
TIME_CALLBACK_FUNCTION | TIME_PERIODIC | TIME_KILL_SYNCHRONOUS);
|
||||
} else if (interval >= 20000u || t->timerType == Qt::VeryCoarseTimer) {
|
||||
// round the interval, VeryCoarseTimers only have full second accuracy
|
||||
interval = ((interval + 500)) / 1000 * 1000;
|
||||
}
|
||||
|
||||
if (ok == 0) {
|
||||
// user normal timers for (Very)CoarseTimers, or if no more multimedia timers available
|
||||
ok = SetTimer(internalHwnd, t->timerId, interval, 0);
|
||||
}
|
||||
|
||||
t->timeout = qt_msectime() + interval;
|
||||
|
||||
if (ok == 0)
|
||||
qErrnoWarning("QEventDispatcherWin32::registerTimer: Failed to create a timer");
|
||||
}
|
||||
@ -611,6 +619,9 @@ void QEventDispatcherWin32Private::sendTimerEvent(int timerId)
|
||||
// send event, but don't allow it to recurse
|
||||
t->inTimerEvent = true;
|
||||
|
||||
// recalculate next emission
|
||||
calculateNextTimeout(t, qt_msectime());
|
||||
|
||||
QTimerEvent e(t->timerId);
|
||||
QCoreApplication::sendEvent(t->obj, &e);
|
||||
|
||||
|
@ -54,6 +54,8 @@ private slots:
|
||||
void singleShotTimeout();
|
||||
void timeout();
|
||||
void remainingTime();
|
||||
void remainingTimeDuringActivation_data();
|
||||
void remainingTimeDuringActivation();
|
||||
void livelock_data();
|
||||
void livelock();
|
||||
void timerInfiniteRecursion_data();
|
||||
@ -79,14 +81,16 @@ class TimerHelper : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
TimerHelper() : QObject(), count(0)
|
||||
TimerHelper() : QObject(), count(0), remainingTime(-1)
|
||||
{
|
||||
}
|
||||
|
||||
int count;
|
||||
int remainingTime;
|
||||
|
||||
public slots:
|
||||
void timeout();
|
||||
void fetchRemainingTime();
|
||||
};
|
||||
|
||||
void TimerHelper::timeout()
|
||||
@ -94,6 +98,12 @@ void TimerHelper::timeout()
|
||||
++count;
|
||||
}
|
||||
|
||||
void TimerHelper::fetchRemainingTime()
|
||||
{
|
||||
QTimer *timer = static_cast<QTimer *>(sender());
|
||||
remainingTime = timer->remainingTime();
|
||||
}
|
||||
|
||||
void tst_QTimer::zeroTimer()
|
||||
{
|
||||
TimerHelper helper;
|
||||
@ -158,6 +168,53 @@ void tst_QTimer::remainingTime()
|
||||
|
||||
int remainingTime = timer.remainingTime();
|
||||
QVERIFY2(qAbs(remainingTime - 150) < 50, qPrintable(QString::number(remainingTime)));
|
||||
|
||||
// wait for the timer to actually fire now
|
||||
connect(&timer, SIGNAL(timeout()), &QTestEventLoop::instance(), SLOT(exitLoop()));
|
||||
QTestEventLoop::instance().enterLoop(5);
|
||||
QVERIFY(!QTestEventLoop::instance().timeout());
|
||||
QCOMPARE(helper.count, 1);
|
||||
|
||||
// the timer is still active, so it should have a non-zero remaining time
|
||||
remainingTime = timer.remainingTime();
|
||||
QVERIFY2(remainingTime > 150, qPrintable(QString::number(remainingTime)));
|
||||
}
|
||||
|
||||
void tst_QTimer::remainingTimeDuringActivation_data()
|
||||
{
|
||||
QTest::addColumn<bool>("singleShot");
|
||||
QTest::newRow("repeating") << true;
|
||||
QTest::newRow("single-shot") << true;
|
||||
}
|
||||
|
||||
void tst_QTimer::remainingTimeDuringActivation()
|
||||
{
|
||||
QFETCH(bool, singleShot);
|
||||
|
||||
TimerHelper helper;
|
||||
QTimer timer;
|
||||
|
||||
const int timeout = 20; // 20 ms is short enough and should not round down to 0 in any timer mode
|
||||
|
||||
connect(&timer, SIGNAL(timeout()), &helper, SLOT(fetchRemainingTime()));
|
||||
connect(&timer, SIGNAL(timeout()), &QTestEventLoop::instance(), SLOT(exitLoop()));
|
||||
timer.start(timeout);
|
||||
timer.setSingleShot(singleShot);
|
||||
|
||||
QTestEventLoop::instance().enterLoop(5);
|
||||
QVERIFY(!QTestEventLoop::instance().timeout());
|
||||
if (singleShot)
|
||||
QCOMPARE(helper.remainingTime, -1); // timer not running
|
||||
else
|
||||
QCOMPARE(helper.remainingTime, timeout);
|
||||
|
||||
if (!singleShot) {
|
||||
// do it again - see QTBUG-46940
|
||||
helper.remainingTime = -1;
|
||||
QTestEventLoop::instance().enterLoop(5);
|
||||
QVERIFY(!QTestEventLoop::instance().timeout());
|
||||
QCOMPARE(helper.remainingTime, timeout);
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QTimer::livelock_data()
|
||||
|
Loading…
Reference in New Issue
Block a user