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

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

source code of qtbase/src/corelib/kernel/qchronotimer.cpp