Timers: Difference between revisions

From Qt Wiki
Jump to navigation Jump to search
No edit summary
 
No edit summary
Line 1: Line 1:
'''English''' [[QtTimers-SimplifiedChinese|简体中文]] [[QtTimers Bulgarian|Български]]<br />
[[Category:QtInternals]]<br />'''English''' [[QtTimers SimplifiedChinese|简体中文]] [[QtTimers_Bulgarian|Български]]<br />[toc align_right=&quot;yes&amp;quot; depth=&quot;2&amp;quot;]


Written By : Girish Ramakrishnan, ForwardBias Technologies
Written By : Girish Ramakrishnan, ForwardBias Technologies


=The timer <span class="caps">API</span>s=
= The timer APIs =


Qt provides two <span class="caps">API</span>s to work with timer.
Qt provides two APIs to work with timer.<br />* &quot;QObject::startTimer&amp;quot;:http://doc.qt.nokia.com/4.7/qobject.html#startTimer - Creates a recurring timer for use by any QObject subclass and returns the timer id. When the timer expires it receives a QEvent::Timer that can be handled by overriding QObject::timerEvent(QTimerEvent '''). The QTimerEvent argument contains the timer id which can be matched against if the QObject uses multiple timers. QObject::killTimer(id) can be used to stop and disarm the timer.
<br />''' &quot;QTimer&amp;quot;:http://doc.qt.nokia.com/latest/qtimer.html - QTimer is a QObject which emits the elapsed() signal when the timer expires. It simply uses QObject::startTimer() and in the QObject::timerEvent() event handler, it emits the elapsed() signal.


* [http://doc.qt.nokia.com/4.7/qobject.html#startTimer QObject::startTimer] ''[doc.qt.nokia.com]'' – Creates a recurring timer for use by any QObject subclass and returns the timer id. When the timer expires it receives a QEvent::Timer that can be handled by overriding QObject::timerEvent(QTimerEvent *). The QTimerEvent argument contains the timer id which can be matched against if the QObject uses multiple timers. QObject::killTimer(id) can be used to stop and disarm the timer.
== QAbstractEventDispatcher ==


* [http://doc.qt.nokia.com/latest/qtimer.html QTimer] ''[doc.qt.nokia.com]'' – QTimer is a QObject which emits the elapsed() signal when the timer expires. It simply uses QObject::startTimer() and in the QObject::timerEvent() event handler, it emits the elapsed() signal.
&quot;QtEventProcessing&amp;quot;:http://developer.qt.nokia.com/wiki/QtEventProcessing provides an overview of how platform events are processed by Qt.


==QAbstractEventDispatcher==
&quot;QAbstractEventDispatcher&amp;quot;:http://doc.qt.nokia.com/latest/qabstracteventdispatcher.html is the interface for the event/message pump. An event dispatcher exists for each thread created using Qt. It requires &quot;registerTimer&amp;quot;:http://doc.qt.nokia.com/latest/qabstracteventdispatcher.html#registerTimer-2 and &quot;unregisterTimer&amp;quot;:http://doc.qt.nokia.com/latest/qabstracteventdispatcher.html#unregisterTimer to be implemented for timers.


[http://developer.qt.nokia.com/wiki/QtEventProcessing QtEventProcessing] ''[developer.qt.nokia.com]'' provides an overview of how platform events are processed by Qt.
There are two versions of the registerTimer (one virtual and one non-virtual). Both versions take an argument to QObject that the timer is to be associated with. The non-virtual registerTimer (cross-platform) allocates a unique timer id and calls the virtual registerTimer for actually registering/creating the timer in a platform-specific way. With this approach, the platform event dispatcher maintains the list of timers associated with a QObject. &quot;QAbstractEventDispatcher::registeredTimers&amp;quot;:http://doc.qt.nokia.com/latest/qabstracteventdispatcher.html#registeredTimers can be used to query the list of timers associated with a QObject.


[http://doc.qt.nokia.com/latest/qabstracteventdispatcher.html QAbstractEventDispatcher] ''[doc.qt.nokia.com]'' is the interface for the event/message pump. An event dispatcher exists for each thread created using Qt. It requires [http://doc.qt.nokia.com/latest/qabstracteventdispatcher.html#registerTimer-2 registerTimer] ''[doc.qt.nokia.com]'' and [http://doc.qt.nokia.com/latest/qabstracteventdispatcher.html#unregisterTimer unregisterTimer] ''[doc.qt.nokia.com]'' to be implemented for timers.
At the low level, once you register a timer, its interval cannot be changed and should be considered as immutable. A high-level API has to create a new timer to change the interval. Also, timers keep firing (i.e recurring) until killed and there is no concept of a &quot;single shot&amp;quot; in the event dispatcher interface. High level APIs have to emulate a single shot timer by unregistering the timer after the first interval expires.


There are two versions of the registerTimer (one virtual and one non-virtual). Both versions take an argument to QObject that the timer is to be associated with. The non-virtual registerTimer (cross-platform) allocates a unique timer id and calls the virtual registerTimer for actually registering/creating the timer in a platform-specific way. With this approach, the platform event dispatcher maintains the list of timers associated with a QObject. [http://doc.qt.nokia.com/latest/qabstracteventdispatcher.html#registeredTimers QAbstractEventDispatcher::registeredTimers] ''[doc.qt.nokia.com]'' can be used to query the list of timers associated with a QObject.
== Timer id allocation algorithm ==


At the low level, once you register a timer, its interval cannot be changed and should be considered as immutable. A high-level <span class="caps">API</span> has to create a new timer to change the interval. Also, timers keep firing (i.e recurring) until killed and there is no concept of a “single shot” in the event dispatcher interface. High level <span class="caps">API</span>s have to emulate a single shot timer by unregistering the timer after the first interval expires.
The algorithm for timer id allocation is lock-free and can be studied from &quot;Brad's blog&amp;quot;:http://labs.trolltech.com/blogs/2008/10/22/a-never-ending-struggle/.


==Timer id allocation algorithm==
An important fact is that timer ids are required to be unique even across threads. This is because when a QObject moves from one thread to another, its timers also move along with it (i.e signals and events are now delivered through the new thread's event loop). Moving the timer is simply a matter of unregistering the timer from the old thread's dispatcher and registering in the new thread's dispatcher. If the timer ids were not unique, the timer ids might clash with existing timers in the new thread. If fresh timer ids were to be regenerated on a QObject::moveToThread, then the application needs to be notified about the id changes and this is a hassle for the application programmer.


The algorithm for timer id allocation is lock-free and can be studied from [http://labs.trolltech.com/blogs/2008/10/22/a-never-ending-struggle/ Brad’s blog] ''[labs.trolltech.com]''.
== Unix (without glib) ==


An important fact is that timer ids are required to be unique even across threads. This is because when a QObject moves from one thread to another, its timers also move along with it (i.e signals and events are now delivered through the new thread’s event loop). Moving the timer is simply a matter of unregistering the timer from the old thread’s dispatcher and registering in the new thread’s dispatcher. If the timer ids were not unique, the timer ids might clash with existing timers in the new thread. If fresh timer ids were to be regenerated on a QObject::moveToThread, then the application needs to be notified about the id changes and this is a hassle for the application programmer.
On Unix, QEventDispatcherUNIX implements the event dispatcher. It maintains a list of timers sorted by their expiry time. When a timer is registered, it's expiry time is computed by adding the timeout value to monotonic clock (clock_gettime). On systems that do not support monotonic clock, gettimeofday is used. The timer is then inserted into a list sorted based on its expiry interval (first item expires first).


==Unix (without glib)==
The dispatcher relies on select() to wait for events on all fds (the X connection, sockets). It first updates the expiry interval for all the registered timers when processEvents() is entered. It then provides the timeout to the select() system call as the interval in the first item of the timer list. After returning from select, the dispatcher &quot;activates&amp;quot; expired timers by sending the associated QObjects a QEvent::Timer.


On Unix, QEventDispatcherUNIX implements the event dispatcher. It maintains a list of timers sorted by their expiry time. When a timer is registered, it’s expiry time is computed by adding the timeout value to monotonic clock (clock_gettime). On systems that do not support monotonic clock, gettimeofday is used. The timer is then inserted into a list sorted based on its expiry interval (first item expires first).
Note that Qt does not use the POSIX timer API - timerfd_create (Why? Portability?).


The dispatcher relies on select() to wait for events on all fds (the X connection, sockets). It first updates the expiry interval for all the registered timers when processEvents() is entered. It then provides the timeout to the select() system call as the interval in the first item of the timer list. After returning from select, the dispatcher “activates” expired timers by sending the associated QObjects a QEvent::Timer.
== Unix (with glib) ==


Note that Qt does not use the <span class="caps">POSIX</span> timer <span class="caps">API</span> – timerfd_create (Why? Portability?).
Qt can be compiled to use the glib event loop (the default) which helps it integrate better with Gtk. &quot;Glib loop documentation&amp;quot;:http://developer.symbian.org/main/documentation/reference/s3/pdk/GUID-7FD05006-09C1-4EF4-A2EB-AD98C2FA8866.html&amp;quot; provides an introduction to how glib loops are written.
 
==Unix (with glib)==
 
Qt can be compiled to use the glib event loop (the default) which helps it integrate better with Gtk. [http://developer.symbian.org/main/documentation/reference/s3/pdk/GUID-7FD05006-09C1-4EF4-A2EB-AD98C2FA8866.html Glib loop documentation] ''[developer.symbian.org]''” provides an introduction to how glib loops are written.


Just like QEventDispatcherUNIX, QEventDispatcherGlib manages timers in a list sorted on expiry time. It creates a new GSource for timers. The prepare, check and dispatch functions of this custom GSource work on the timer list in the obvious way.
Just like QEventDispatcherUNIX, QEventDispatcherGlib manages timers in a list sorted on expiry time. It creates a new GSource for timers. The prepare, check and dispatch functions of this custom GSource work on the timer list in the obvious way.


==Windows==
== Windows ==


As noted in [http://developer.qt.nokia.com/wiki/QtEventProcessing QtEventProcessing] ''[developer.qt.nokia.com]'', Qt creates a hidden window with a callback function to process events. When a timer is registered, QEventDispatcherWin creates a native Windows timer based on the timer interval.
As noted in &quot;QtEventProcessing&amp;quot;:http://developer.qt.nokia.com/wiki/QtEventProcessing, Qt creates a hidden window with a callback function to process events. When a timer is registered, QEventDispatcherWin creates a native Windows timer based on the timer interval.


* If the interval is greater than 20 msecs, it uses SetTimer (with the id that was generated by QAbstractEventDispatcher::registerTimer). SetTimer sends a WM_TIMER message to the callback function that gets sent as QEvent::Timer to the QObject.
* If the interval is greater than 20 msecs, it uses SetTimer (with the id that was generated by QAbstractEventDispatcher::registerTimer). SetTimer sends a WM_TIMER message to the callback function that gets sent as QEvent::Timer to the QObject.


* If the interval is less than 20 msecs, Qt tries to use multimedia (aka fast) timers using timeSetEvent. timeSetEvent takes a callback function that is called on expiry and will be called from a separate thread. In Qt, timers fire in the same thread as that of the QObject. So the timeSetEvent’s callback function posts a message to the dispatcher which the dispatcher picks up and sends as QEvent::Timer.
* If the interval is less than 20 msecs, Qt tries to use multimedia (aka fast) timers using timeSetEvent. timeSetEvent takes a callback function that is called on expiry and will be called from a separate thread. In Qt, timers fire in the same thread as that of the QObject. So the timeSetEvent's callback function posts a message to the dispatcher which the dispatcher picks up and sends as QEvent::Timer.


* If the interval is 0, the dispatcher posts a special message to itself (QEvent::ZeroTimerEvent) on registration. The dispatcher processes this event by sending a QEvent::Timer to the corresponding QObject and posts a ZeroTimerEvent to itself.
* If the interval is 0, the dispatcher posts a special message to itself (QEvent::ZeroTimerEvent) on registration. The dispatcher processes this event by sending a QEvent::Timer to the corresponding QObject and posts a ZeroTimerEvent to itself.


==<span class="caps">API</span> implementation==
== API implementation ==


QObject::startTimer registers a new timer with event dispatcher. This is roughly the equivalent of, QAbstractEventDispatcher::instance(object-&gt;thread())-&gt;registerTimer(interval, object).
QObject::startTimer registers a new timer with event dispatcher. This is roughly the equivalent of, QAbstractEventDispatcher::instance(object-&gt;thread())-&gt;registerTimer(interval, object).


QTimer is a QObject. All it does is call QObject::startTimer(). It emits activated() signal in it’s timerEvent().
QTimer is a QObject. All it does is call QObject::startTimer(). It emits activated() signal in it's timerEvent().
 
==QBasicTimer==
 
QTimer feels heavy since it’s a QObject and in general Qt code tries to avoid it. At the same time, using the QObject::startTimer() <span class="caps">API</span> is a bit error-prone.
 
# When stopping a timer, you have to invalidate the timer id (say to -1) after QObject::killTimer(m_timerId). This is required since a positive value for m_timerId will indicate to your code that the timer is active.
 
# To restart a timer, one needs to remember to kill the old timer if m_timerId is not -1.
 
QBasicTimer solves the above and is just a glorified interface to the integer “timer id”. QBasicTimer::start(int msec, QObject *) kills any existing timer and create a new one using object-&gt;startTimer(msec). QBasicTimer::stop() will kill the timer and invalidate the id. QBasicTimer::timerId() provides the timer id.


==Further reading==
== QBasicTimer ==


* [http://labs.trolltech.com/blogs/2008/10/22/a-never-ending-struggle/ A never-ending struggle] ''[labs.trolltech.com]'' – Timer id allocation algorithm
QTimer feels heavy since it's a QObject and in general Qt code tries to avoid it. At the same time, using the QObject::startTimer() API is a bit error-prone.<br /># When stopping a timer, you have to invalidate the timer id (say to –1) after QObject::killTimer(m_timerId). This is required since a positive value for m_timerId will indicate to your code that the timer is active.


===Categories:===
# To restart a timer, one needs to remember to kill the old timer if m_timerId is not <s>1.
<br />QBasicTimer solves the above and is just a glorified interface to the integer &quot;timer id&amp;quot;. QBasicTimer::start(int msec, QObject *) kills any existing timer and create a new one using object</s>&gt;startTimer(msec). QBasicTimer::stop() will kill the timer and invalidate the id. QBasicTimer::timerId() provides the timer id.


* [[:Category:QtInternals|QtInternals]]
== Further reading ==

Revision as of 09:31, 24 February 2015


English 简体中文 Български
[toc align_right="yes&quot; depth="2&quot;]

Written By : Girish Ramakrishnan, ForwardBias Technologies

The timer APIs

Qt provides two APIs to work with timer.
* "QObject::startTimer&quot;:http://doc.qt.nokia.com/4.7/qobject.html#startTimer - Creates a recurring timer for use by any QObject subclass and returns the timer id. When the timer expires it receives a QEvent::Timer that can be handled by overriding QObject::timerEvent(QTimerEvent ). The QTimerEvent argument contains the timer id which can be matched against if the QObject uses multiple timers. QObject::killTimer(id) can be used to stop and disarm the timer.
"QTimer&quot;:http://doc.qt.nokia.com/latest/qtimer.html - QTimer is a QObject which emits the elapsed() signal when the timer expires. It simply uses QObject::startTimer() and in the QObject::timerEvent() event handler, it emits the elapsed() signal.

QAbstractEventDispatcher

"QtEventProcessing&quot;:http://developer.qt.nokia.com/wiki/QtEventProcessing provides an overview of how platform events are processed by Qt.

"QAbstractEventDispatcher&quot;:http://doc.qt.nokia.com/latest/qabstracteventdispatcher.html is the interface for the event/message pump. An event dispatcher exists for each thread created using Qt. It requires "registerTimer&quot;:http://doc.qt.nokia.com/latest/qabstracteventdispatcher.html#registerTimer-2 and "unregisterTimer&quot;:http://doc.qt.nokia.com/latest/qabstracteventdispatcher.html#unregisterTimer to be implemented for timers.

There are two versions of the registerTimer (one virtual and one non-virtual). Both versions take an argument to QObject that the timer is to be associated with. The non-virtual registerTimer (cross-platform) allocates a unique timer id and calls the virtual registerTimer for actually registering/creating the timer in a platform-specific way. With this approach, the platform event dispatcher maintains the list of timers associated with a QObject. "QAbstractEventDispatcher::registeredTimers&quot;:http://doc.qt.nokia.com/latest/qabstracteventdispatcher.html#registeredTimers can be used to query the list of timers associated with a QObject.

At the low level, once you register a timer, its interval cannot be changed and should be considered as immutable. A high-level API has to create a new timer to change the interval. Also, timers keep firing (i.e recurring) until killed and there is no concept of a "single shot&quot; in the event dispatcher interface. High level APIs have to emulate a single shot timer by unregistering the timer after the first interval expires.

Timer id allocation algorithm

The algorithm for timer id allocation is lock-free and can be studied from "Brad's blog&quot;:http://labs.trolltech.com/blogs/2008/10/22/a-never-ending-struggle/.

An important fact is that timer ids are required to be unique even across threads. This is because when a QObject moves from one thread to another, its timers also move along with it (i.e signals and events are now delivered through the new thread's event loop). Moving the timer is simply a matter of unregistering the timer from the old thread's dispatcher and registering in the new thread's dispatcher. If the timer ids were not unique, the timer ids might clash with existing timers in the new thread. If fresh timer ids were to be regenerated on a QObject::moveToThread, then the application needs to be notified about the id changes and this is a hassle for the application programmer.

Unix (without glib)

On Unix, QEventDispatcherUNIX implements the event dispatcher. It maintains a list of timers sorted by their expiry time. When a timer is registered, it's expiry time is computed by adding the timeout value to monotonic clock (clock_gettime). On systems that do not support monotonic clock, gettimeofday is used. The timer is then inserted into a list sorted based on its expiry interval (first item expires first).

The dispatcher relies on select() to wait for events on all fds (the X connection, sockets). It first updates the expiry interval for all the registered timers when processEvents() is entered. It then provides the timeout to the select() system call as the interval in the first item of the timer list. After returning from select, the dispatcher "activates&quot; expired timers by sending the associated QObjects a QEvent::Timer.

Note that Qt does not use the POSIX timer API - timerfd_create (Why? Portability?).

Unix (with glib)

Qt can be compiled to use the glib event loop (the default) which helps it integrate better with Gtk. "Glib loop documentation&quot;:http://developer.symbian.org/main/documentation/reference/s3/pdk/GUID-7FD05006-09C1-4EF4-A2EB-AD98C2FA8866.html&quot; provides an introduction to how glib loops are written.

Just like QEventDispatcherUNIX, QEventDispatcherGlib manages timers in a list sorted on expiry time. It creates a new GSource for timers. The prepare, check and dispatch functions of this custom GSource work on the timer list in the obvious way.

Windows

As noted in "QtEventProcessing&quot;:http://developer.qt.nokia.com/wiki/QtEventProcessing, Qt creates a hidden window with a callback function to process events. When a timer is registered, QEventDispatcherWin creates a native Windows timer based on the timer interval.

  • If the interval is greater than 20 msecs, it uses SetTimer (with the id that was generated by QAbstractEventDispatcher::registerTimer). SetTimer sends a WM_TIMER message to the callback function that gets sent as QEvent::Timer to the QObject.
  • If the interval is less than 20 msecs, Qt tries to use multimedia (aka fast) timers using timeSetEvent. timeSetEvent takes a callback function that is called on expiry and will be called from a separate thread. In Qt, timers fire in the same thread as that of the QObject. So the timeSetEvent's callback function posts a message to the dispatcher which the dispatcher picks up and sends as QEvent::Timer.
  • If the interval is 0, the dispatcher posts a special message to itself (QEvent::ZeroTimerEvent) on registration. The dispatcher processes this event by sending a QEvent::Timer to the corresponding QObject and posts a ZeroTimerEvent to itself.

API implementation

QObject::startTimer registers a new timer with event dispatcher. This is roughly the equivalent of, QAbstractEventDispatcher::instance(object->thread())->registerTimer(interval, object).

QTimer is a QObject. All it does is call QObject::startTimer(). It emits activated() signal in it's timerEvent().

QBasicTimer

QTimer feels heavy since it's a QObject and in general Qt code tries to avoid it. At the same time, using the QObject::startTimer() API is a bit error-prone.
# When stopping a timer, you have to invalidate the timer id (say to –1) after QObject::killTimer(m_timerId). This is required since a positive value for m_timerId will indicate to your code that the timer is active.

  1. To restart a timer, one needs to remember to kill the old timer if m_timerId is not 1.


QBasicTimer solves the above and is just a glorified interface to the integer "timer id&quot;. QBasicTimer::start(int msec, QObject *) kills any existing timer and create a new one using object
>startTimer(msec). QBasicTimer::stop() will kill the timer and invalidate the id. QBasicTimer::timerId() provides the timer id.

Further reading