Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qchronotimer.cpp
Go to the documentation of this file.
1// Copyright (C) 2022 The Qt Company Ltd.
2// Copyright (C) 2016 Intel Corporation.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4// Qt-Security score:significant reason:default
5
6#include "qchronotimer.h"
7#include "qtimer_p.h"
9
13#include "qdeadlinetimer.h"
14#include "qmetaobject_p.h"
15#include "qobject_p.h"
16#include "qproperty_p.h"
17#include "qthread.h"
18
19using namespace std::chrono_literals;
20
21QT_BEGIN_NAMESPACE
22
23/*!
24 \class QChronoTimer
25 \inmodule QtCore
26 \since 6.8
27 \ingroup events
28
29 \brief The QChronoTimer class provides repetitive and single-shot timers.
30
31 The QChronoTimer class provides a high-level programming interface for
32 timers. To use it, create a QChronoTimer, either passing the interval to the
33 constructor, or setting it after construction using setInterval(), connect
34 its timeout() signal to the appropriate slots, and call start(). From then
35 on, it will emit the timeout() signal at constant intervals. For example:
36
37 \snippet timers/timers.cpp qchronotimer-interval-in-ctor
38 \snippet timers/timers.cpp qchronotimer-setinterval
39
40 You can set a timer to time out only once by calling setSingleShot(true).
41
42 \note QChronoTimer has no singleShot() static methods, as the ones on
43 QTimer already work with chrono types and nanoseconds resolution.
44
45 In multithreaded applications, you can use QChronoTimer in any thread
46 that has an event loop. To start an event loop from a non-GUI
47 thread, use QThread::exec(). Qt uses the timer's
48 \l{QObject::thread()}{thread affinity} to determine which thread
49 will emit the \l{QChronoTimer::}{timeout()} signal. Because of this, you
50 must start and stop the timer in its thread; it is not possible to
51 start a timer from another thread.
52
53 As a special case, a QChronoTimer with a timeout of \c 0ns will time out
54 as soon as possible, though the ordering between zero timers and other
55 sources of events is unspecified. Zero timers can be used to do some
56 work while still providing a responsive user interface:
57
58 \snippet timers/timers.cpp zero-timer
59
60 From then on, \c processOneThing() will be called repeatedly. It should
61 be written in such a way that it always returns quickly (for example,
62 after processing one data item) so that Qt can deliver events to the user
63 interface and stop the timer as soon as it has done all its work. This
64 is the traditional way of implementing heavy work in GUI applications,
65 but as multithreading is becoming available on more platforms, a modern
66 alternative is doing the heavy work in a thread other than the GUI (main)
67 thread. Qt has the QThread class, which can be used to achieve that.
68
69 \section1 Accuracy and Timer Resolution
70
71 The accuracy of timers depends on the underlying operating system and
72 hardware. Most platforms support requesting nano-second precision for
73 timers (for example, libc's \c nanosleep), though the accuracy of the
74 timer will not equal this resolution in many real-world situations.
75
76 You can set the \l{Qt::TimerType}{timer type} to tell QChronoTimer which
77 precision to request from the system.
78
79 For Qt::PreciseTimer, QChronoTimer will try to keep the precision at
80 \c 1ns. Precise timers will never time out earlier than expected.
81
82 For Qt::CoarseTimer and Qt::VeryCoarseTimer types, QChronoTimer may wake
83 up earlier than expected, within the margins for those types:
84 \list
85 \li 5% of the interval for Qt::CoarseTimer
86 \li \c 500ms for Qt::VeryCoarseTimer
87 \endlist
88
89 All timer types may time out later than expected if the system is busy or
90 unable to provide the requested accuracy. In such a case of timeout
91 overrun, Qt will emit timeout() only once, even if multiple timeouts have
92 expired, and then will resume the original interval.
93
94 \section1 Alternatives to QChronoTimer
95
96 QChronoTimer provides nanosecond resolution and a ±292 years range
97 (less chances of integer overflow if the interval is longer than \c
98 std::numeric_limits<int>::max()). If you only need millisecond resolution
99 and ±24 days range, you can continue to use the classical QTimer class
100
101 \include timers-common.qdocinc q-chrono-timer-alternatives
102
103 Some operating systems limit the number of timers that may be used;
104 Qt does its best to work around these limitations.
105
106 \sa QBasicTimer, QTimerEvent, QObject::timerEvent(), Timers,
107 {Analog Clock}
108*/
109
110/*!
111 Constructs a timer with the given \a parent, using the default interval,
112 \c 0ns.
113*/
114QChronoTimer::QChronoTimer(QObject *parent)
115 : QChronoTimer(0ns, parent)
116{
117}
118
119/*!
120 Constructs a timer with the given \a parent, using an interval of \a nsec.
121*/
122QChronoTimer::QChronoTimer(std::chrono::nanoseconds nsec, QObject *parent)
123 : QObject(*new QTimerPrivate(nsec, this), parent)
124{
125 Q_ASSERT(!d_func()->isQTimer);
126}
127
128/*!
129 Destroys the timer.
130*/
131QChronoTimer::~QChronoTimer()
132{
133 if (d_func()->isActive()) // stop running timer
134 stop();
135}
136
137/*!
138 \fn void QChronoTimer::timeout()
139
140 This signal is emitted when the timer times out.
141
142 \sa interval, start(), stop()
143*/
144
145/*!
146 \property QChronoTimer::active
147
148 This boolean property is \c true if the timer is running; otherwise
149 \c false.
150*/
151
152/*!
153 Returns \c true if the timer is running; otherwise returns \c false.
154*/
155bool QChronoTimer::isActive() const
156{
157 return d_func()->isActiveData.value();
158}
159
160QBindable<bool> QChronoTimer::bindableActive()
161{
162 return QBindable<bool>(&d_func()->isActiveData);
163}
164
165/*!
166 Returns a Qt::TimerId representing the timer ID if the timer is running;
167 otherwise returns \c Qt::TimerId::Invalid.
168
169 \sa Qt::TimerId
170*/
171Qt::TimerId QChronoTimer::id() const
172{
173 return d_func()->id;
174}
175
176/*! \overload start()
177
178 Starts or restarts the timer with the timeout specified in \l interval.
179
180//! [stop-restart-timer]
181 If the timer is already running, it will be
182 \l{QChronoTimer::stop()}{stopped} and restarted. This will also change its
183 id().
184//! [stop-restart-timer]
185
186 If \l singleShot is true, the timer will be activated only once.
187*/
188void QChronoTimer::start()
189{
190 auto *d = d_func();
191 if (d->isActive()) // stop running timer
192 stop();
193 const auto id = Qt::TimerId{QObject::startTimer(d->intervalDuration, d->type)};
194 if (id != Qt::TimerId::Invalid) {
195 d->id = id;
196 d->isActiveData.notify();
197 }
198}
199
200/*!
201 Stops the timer.
202
203 \sa start()
204*/
205void QChronoTimer::stop()
206{
207 auto *d = d_func();
208 if (d->isActive()) {
209 QObject::killTimer(d->id);
210 d->id = Qt::TimerId::Invalid;
211 d->isActiveData.notify();
212 }
213}
214
215/*!
216 \reimp
217*/
218void QChronoTimer::timerEvent(QTimerEvent *e)
219{
220 auto *d = d_func();
221 if (e->id() == d->id) {
222 if (d->single)
223 stop();
224 Q_EMIT timeout(QPrivateSignal());
225 }
226}
227
228/*!
229 \fn template <typename Functor> QMetaObject::Connection QChronoTimer::callOnTimeout(const QObject *context, Functor &&slot, Qt::ConnectionType connectionType = Qt::AutoConnection)
230 \overload callOnTimeout()
231
232 Creates a connection from the timeout() signal to \a slot to be placed in a
233 specific event loop of \a context, with connection type \a connectionType,
234 and returns a handle to the connection.
235
236 This method is provided as a convenience. It's equivalent to calling:
237 \code
238 QObject::connect(timer, &QChronoTimer::timeout, context, slot, connectionType);
239 \endcode
240
241 \sa QObject::connect(), timeout()
242*/
243
244/*!
245 \property QChronoTimer::singleShot
246 \brief Whether the timer is a single-shot timer
247
248 A single-shot timer fires only once, non-single-shot timers fire every
249 \l interval.
250
251 The default value for this property is \c false.
252
253 \sa interval
254*/
255void QChronoTimer::setSingleShot(bool singleShot)
256{
257 d_func()->single = singleShot;
258}
259
260bool QChronoTimer::isSingleShot() const
261{
262 return d_func()->single;
263}
264
265QBindable<bool> QChronoTimer::bindableSingleShot()
266{
267 return QBindable<bool>(&d_func()->single);
268}
269
270/*!
271 \property QChronoTimer::interval
272 \brief The timeout interval
273
274 The default value for this property is \c 0ns.
275
276 A QChronoTimer with a timeout of \c 0ns will time out as soon as all
277 the events in the window system's event queue have been processed.
278
279 Setting the interval of a running timer will change the interval,
280 stop() and then start() the timer, and acquire a new id().
281 If the timer is not running, only the interval is changed.
282
283 \include timers-common.qdocinc negative-intervals-not-allowed
284
285 \sa singleShot
286*/
287void QChronoTimer::setInterval(std::chrono::nanoseconds nsec)
288{
289 if (nsec < 0ns) {
290 qWarning("QChronoTimer::setInterval: negative intervals aren't allowed; the "
291 "interval will be set to 1ms.");
292 nsec = 1ms;
293 }
294
295 auto *d = d_func();
296 d->intervalDuration.removeBindingUnlessInWrapper();
297 const bool intervalChanged = nsec != d->intervalDuration.valueBypassingBindings();
298 d->intervalDuration.setValueBypassingBindings(nsec);
299 if (d->isActive()) { // Create new timer
300 QObject::killTimer(d->id); // Restart timer
301 const auto newId = Qt::TimerId{QObject::startTimer(nsec, d->type)};
302 if (newId != Qt::TimerId::Invalid) {
303 // Restarted successfully. No need to update the active state.
304 d->id = newId;
305 } else {
306 // Failed to start the timer.
307 // Need to notify about active state change.
308 d->id = Qt::TimerId::Invalid;
309 d->isActiveData.notify();
310 }
311 }
312 if (intervalChanged)
313 d->intervalDuration.notify();
314}
315
316std::chrono::nanoseconds QChronoTimer::interval() const
317{
318 return d_func()->intervalDuration.value();
319}
320
321QBindable<std::chrono::nanoseconds> QChronoTimer::bindableInterval()
322{
323 return {&d_func()->intervalDuration};
324}
325
326/*!
327 \property QChronoTimer::remainingTime
328 \brief The remaining time
329
330 Returns the remaining duration until the timeout.
331
332 If the timer is inactive, the returned duration will be negative.
333
334 If the timer is overdue, the returned duration will be \c 0ns.
335
336 \sa interval
337*/
338std::chrono::nanoseconds QChronoTimer::remainingTime() const
339{
340 if (isActive())
341 return QAbstractEventDispatcher::instance()->remainingTime(d_func()->id);
342 return std::chrono::nanoseconds::min();
343}
344
345/*!
346 \property QChronoTimer::timerType
347 \brief Controls the accuracy of the timer
348
349 The default value for this property is \c Qt::CoarseTimer.
350
351 \sa Qt::TimerType
352*/
353void QChronoTimer::setTimerType(Qt::TimerType atype)
354{
355 d_func()->type = atype;
356}
357
358Qt::TimerType QChronoTimer::timerType() const
359{
360 return d_func()->type;
361}
362
363QBindable<Qt::TimerType> QChronoTimer::bindableTimerType()
364{
365 return {&d_func()->type};
366}
367
368QT_END_NAMESPACE
369
370#include "moc_qchronotimer.cpp"