summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThiago Macieira <thiago.macieira@intel.com>2015-06-30 18:15:53 -0700
committerThiago Macieira <thiago.macieira@intel.com>2015-07-08 03:20:29 +0000
commitd01d08971a1ba4d66927a48577041abc5b7df8a2 (patch)
tree7274e52fa8ec9b374bc9b7b5a66746384e8f886d
parent1ac0c4a2f787607269c2b5511a0f9a410d2f5603 (diff)
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>
-rw-r--r--src/corelib/kernel/qeventdispatcher_win.cpp21
-rw-r--r--tests/auto/corelib/kernel/qtimer/tst_qtimer.cpp59
2 files changed, 74 insertions, 6 deletions
diff --git a/src/corelib/kernel/qeventdispatcher_win.cpp b/src/corelib/kernel/qeventdispatcher_win.cpp
index b55679f3d5..6da70cf0bd 100644
--- a/src/corelib/kernel/qeventdispatcher_win.cpp
+++ b/src/corelib/kernel/qeventdispatcher_win.cpp
@@ -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);
diff --git a/tests/auto/corelib/kernel/qtimer/tst_qtimer.cpp b/tests/auto/corelib/kernel/qtimer/tst_qtimer.cpp
index c7011dbc04..1d1432f600 100644
--- a/tests/auto/corelib/kernel/qtimer/tst_qtimer.cpp
+++ b/tests/auto/corelib/kernel/qtimer/tst_qtimer.cpp
@@ -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()