From 504dd08854a8f6519a0529014df2233938b82728 Mon Sep 17 00:00:00 2001 From: Shane Kearns Date: Wed, 16 Sep 2009 15:48:59 +0200 Subject: Fix QTimer overflow with interval > 35 minutes on symbian For long intervals, restart the system timer every 2000 seconds New autotest to verify long timers don't crash or complete immediately Task-number: QT-651 Reviewed-by: axis --- src/corelib/kernel/qeventdispatcher_symbian.cpp | 33 +++++++++++++++++++++---- src/corelib/kernel/qeventdispatcher_symbian_p.h | 2 ++ tests/auto/qtimer/tst_qtimer.cpp | 12 +++++++++ 3 files changed, 42 insertions(+), 5 deletions(-) diff --git a/src/corelib/kernel/qeventdispatcher_symbian.cpp b/src/corelib/kernel/qeventdispatcher_symbian.cpp index b1c8734..11a0da6 100644 --- a/src/corelib/kernel/qeventdispatcher_symbian.cpp +++ b/src/corelib/kernel/qeventdispatcher_symbian.cpp @@ -231,15 +231,39 @@ void QTimerActiveObject::RunL() } } +#define MAX_SYMBIAN_TIMEOUT_MS 2000000 +void QTimerActiveObject::StartTimer() +{ + if (m_timerInfo->msLeft > MAX_SYMBIAN_TIMEOUT_MS) { + //There is loss of accuracy anyway due to needing to restart the timer every 33 minutes, + //so the 1/64s res of After() is acceptable for these very long timers. + m_rTimer.After(iStatus, MAX_SYMBIAN_TIMEOUT_MS * 1000); + m_timerInfo->msLeft -= MAX_SYMBIAN_TIMEOUT_MS; + } else { + //HighRes gives the 1ms accuracy expected by Qt, the +1 is to ensure that + //"Timers will never time out earlier than the specified timeout value" + //condition is always met. + m_rTimer.HighRes(iStatus, (m_timerInfo->msLeft + 1) * 1000); + m_timerInfo->msLeft = 0; + } + SetActive(); +} + void QTimerActiveObject::Run() { + //restart timer immediately, if the timeout has been split because it overflows max for platform. + if (m_timerInfo->msLeft > 0) { + StartTimer(); + return; + } + if (!okToRun()) return; if (m_timerInfo->interval > 0) { // Start a new timer immediately so that we don't lose time. - SetActive(); - m_rTimer.After(iStatus, m_timerInfo->interval*1000); + m_timerInfo->msLeft = m_timerInfo->interval; + StartTimer(); m_timerInfo->dispatcher->timerFired(m_timerInfo->timerId); } else { @@ -261,11 +285,10 @@ void QTimerActiveObject::Run() void QTimerActiveObject::Start() { CActiveScheduler::Add(this); + m_timerInfo->msLeft = m_timerInfo->interval; if (m_timerInfo->interval > 0) { m_rTimer.CreateLocal(); - iStatus = KRequestPending; - SetActive(); - m_rTimer.After(iStatus, m_timerInfo->interval*1000); + StartTimer(); } else { iStatus = KRequestPending; SetActive(); diff --git a/src/corelib/kernel/qeventdispatcher_symbian_p.h b/src/corelib/kernel/qeventdispatcher_symbian_p.h index 53f92a9..fd0350d 100644 --- a/src/corelib/kernel/qeventdispatcher_symbian_p.h +++ b/src/corelib/kernel/qeventdispatcher_symbian_p.h @@ -117,6 +117,7 @@ struct SymbianTimerInfo : public QSharedData int timerId; int interval; + int msLeft; bool inTimerEvent; QObject *receiver; QTimerActiveObject *timerAO; @@ -140,6 +141,7 @@ protected: private: void Run(); + void StartTimer(); private: SymbianTimerInfo *m_timerInfo; diff --git a/tests/auto/qtimer/tst_qtimer.cpp b/tests/auto/qtimer/tst_qtimer.cpp index a55c1c6..0877500 100644 --- a/tests/auto/qtimer/tst_qtimer.cpp +++ b/tests/auto/qtimer/tst_qtimer.cpp @@ -88,6 +88,7 @@ private slots: void timerFiresOnlyOncePerProcessEvents_data(); void timerFiresOnlyOncePerProcessEvents(); void timerIdPersistsAfterThreadExit(); + void cancelLongTimer(); }; class TimerHelper : public QObject @@ -602,5 +603,16 @@ void tst_QTimer::timerIdPersistsAfterThreadExit() QVERIFY((timerId & 0xffffff) != (thread.timerId & 0xffffff)); } +void tst_QTimer::cancelLongTimer() +{ + QTimer timer; + timer.setSingleShot(true); + timer.start(1000 * 60 * 60); //set timer for 1 hour (which would overflow Symbian RTimer) + QCoreApplication::processEvents(); + QVERIFY(timer.isActive()); //if the timer completes immediately with an error, then this will fail + timer.stop(); + QVERIFY(!timer.isActive()); +} + QTEST_MAIN(tst_QTimer) #include "tst_qtimer.moc" -- cgit v0.12