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
277 possible, though the ordering between zero timers and other sources of
278 events is unspecified.
279
280 Setting the interval of a running timer will change the interval,
281 stop() and then start() the timer, and acquire a new id().
282 If the timer is not running, only the interval is changed.
283
284 \include timers-common.qdocinc negative-intervals-not-allowed
285
286 \sa singleShot
287*/
288void QChronoTimer::setInterval(std::chrono::nanoseconds nsec)
289{
290 if (nsec < 0ns) {
291 qWarning("QChronoTimer::setInterval: negative intervals aren't allowed; the "
292 "interval will be set to 1ms.");
293 nsec = 1ms;
294 }
295
296 auto *d = d_func();
297 d->intervalDuration.removeBindingUnlessInWrapper();
298 const bool intervalChanged = nsec != d->intervalDuration.valueBypassingBindings();
299 d->intervalDuration.setValueBypassingBindings(nsec);
300 if (d->isActive()) { // Create new timer
301 QObject::killTimer(d->id); // Restart timer
302 const auto newId = Qt::TimerId{QObject::startTimer(nsec, d->type)};
303 if (newId != Qt::TimerId::Invalid) {
304 // Restarted successfully. No need to update the active state.
305 d->id = newId;
306 } else {
307 // Failed to start the timer.
308 // Need to notify about active state change.
309 d->id = Qt::TimerId::Invalid;
310 d->isActiveData.notify();
311 }
312 }
313 if (intervalChanged)
314 d->intervalDuration.notify();
315}
316
317std::chrono::nanoseconds QChronoTimer::interval() const
318{
319 return d_func()->intervalDuration.value();
320}
321
322QBindable<std::chrono::nanoseconds> QChronoTimer::bindableInterval()
323{
324 return {&d_func()->intervalDuration};
325}
326
327/*!
328 \property QChronoTimer::remainingTime
329 \brief The remaining time
330
331 Returns the remaining duration until the timeout.
332
333 If the timer is inactive, the returned duration will be negative.
334
335 If the timer is overdue, the returned duration will be \c 0ns.
336
337 \sa interval
338*/
339std::chrono::nanoseconds QChronoTimer::remainingTime() const
340{
341 if (isActive())
342 return QAbstractEventDispatcher::instance()->remainingTime(d_func()->id);
343 return std::chrono::nanoseconds::min();
344}
345
346/*!
347 \property QChronoTimer::timerType
348 \brief Controls the accuracy of the timer
349
350 The default value for this property is \c Qt::CoarseTimer.
351
352 \sa Qt::TimerType
353*/
354void QChronoTimer::setTimerType(Qt::TimerType atype)
355{
356 d_func()->type = atype;
357}
358
359Qt::TimerType QChronoTimer::timerType() const
360{
361 return d_func()->type;
362}
363
364QBindable<Qt::TimerType> QChronoTimer::bindableTimerType()
365{
366 return {&d_func()->type};
367}
368
369QT_END_NAMESPACE
370
371#include "moc_qchronotimer.cpp"