summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAhmad Samir <a.samirh78@gmail.com>2023-07-11 16:32:39 +0300
committerAhmad Samir <a.samirh78@gmail.com>2024-03-03 19:56:55 +0200
commitbd764cc1ca578759f16fbe292fbe14a243b7d263 (patch)
tree9e270f40316c60a3b536e2019fc70d63cadc0224
parent4fa9034d0c592e5f07531d41463c8c462f5e8895 (diff)
Add QChronoTimer, a timer with nanoseconds precision
The interval in QTimer is a QProperty of type int, which means it's limited to the number of milliseconds that would fit in an int (~24 days), this could cause overflow if a user constructs a QTimer with an interval > INT_MAX milliseconds. And it can't be easily changed to use qint64/std::chrono::nanoseconds: - changing the getters to return qint64 means user code would have narrowing conversions - the bindable QProperty interval can't be changed to qint64 during Qt6's lifetime without the risk of breaking user code - adding a new bindable QProperty that is qint64/nanoseconds is an option, but it has the complication of what to do with the int interval; set it when setInterval(milliseconds) is used by using saturation arithmetic? and what about notifying observers of the changed interval? Thus the idea of creating a new stop-gap class, QChronoTimer, as a cleaner solution. Both classes use QTimerPrivate. During the lifetime of Qt6, QTimer's interval range is about 24 days, whereas QChronoTimer's interval range is about 292 years (duration_cast<years>nanoseconds::max()). Currently the plan is to fold QChronotTimer back into QTimer in Qt7. Mark all QPropertyS in the new class as FINAL since they aren't intended to be overridden; this offers a performance boost for QML[1]. [1] https://lists.qt-project.org/pipermail/development/2024-February/044977.html [ChangeLog][QtCore] Added QChronoTimer, which uses a std::chrono::nanoseconds intervals, as a replacement for QTimer. Fixes: QTBUG-113544 Change-Id: I71697f4a8b35452c6b5604b1322ee7f0b4453f04 Reviewed-by: Thiago Macieira <thiago.macieira@intel.com> Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
-rw-r--r--src/corelib/CMakeLists.txt1
-rw-r--r--src/corelib/doc/snippets/timers/timers.cpp44
-rw-r--r--src/corelib/kernel/qchronotimer.cpp645
-rw-r--r--src/corelib/kernel/qchronotimer.h178
-rw-r--r--src/corelib/kernel/qobjectdefs.h1
-rw-r--r--src/corelib/kernel/qtimer.cpp3
-rw-r--r--src/corelib/kernel/qtimer_p.h38
-rw-r--r--tests/auto/corelib/kernel/CMakeLists.txt1
-rw-r--r--tests/auto/corelib/kernel/qchronotimer/.gitignore2
-rw-r--r--tests/auto/corelib/kernel/qchronotimer/CMakeLists.txt15
-rw-r--r--tests/auto/corelib/kernel/qchronotimer/tst_qchronotimer.cpp1035
-rw-r--r--tests/auto/gui/kernel/CMakeLists.txt1
-rw-r--r--tests/auto/gui/kernel/qguichronotimer/CMakeLists.txt20
13 files changed, 776 insertions, 1208 deletions
diff --git a/src/corelib/CMakeLists.txt b/src/corelib/CMakeLists.txt
index fa18010965..f1f38c95ff 100644
--- a/src/corelib/CMakeLists.txt
+++ b/src/corelib/CMakeLists.txt
@@ -153,6 +153,7 @@ qt_internal_add_module(Core
kernel/qassociativeiterable.cpp kernel/qassociativeiterable.h
kernel/qbasictimer.cpp kernel/qbasictimer.h
kernel/qbindingstorage.h
+ kernel/qchronotimer.cpp kernel/qchronotimer.h
kernel/qcoreapplication.cpp kernel/qcoreapplication.h kernel/qcoreapplication_p.h
kernel/qcoreapplication_platform.h
kernel/qcorecmdlineargs_p.h
diff --git a/src/corelib/doc/snippets/timers/timers.cpp b/src/corelib/doc/snippets/timers/timers.cpp
index c89db6890c..84618adb81 100644
--- a/src/corelib/doc/snippets/timers/timers.cpp
+++ b/src/corelib/doc/snippets/timers/timers.cpp
@@ -1,8 +1,12 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+#include <QChronoTimer>
+#include <QObject>
#include <QTimer>
+using namespace std::chrono;
+
class Foo : public QObject
{
public:
@@ -35,7 +39,45 @@ Foo::Foo()
}
}
-int main()
+// QChronoTimer
+class MyWidget : QObject
{
+ MyWidget()
+ {
+//! [qchronotimer-singleshot]
+ MyWidget widget;
+ QChronoTimer::singleShot(100ms, &widget, &MyWidget::processOneThing);
+//! [qchronotimer-singleshot]
+//! [zero-timer]
+ // The default interval is 0ns
+ QChronoTimer *timer = new QChronoTimer(this);
+ connect(timer, &QChronoTimer::timeout, this, &MyWidget::processOneThing);
+ timer->start();
+//! [zero-timer]
+
+ {
+//! [timer-interval-in-ctor]
+ QChronoTimer *timer = new QChronoTimer(1s, this);
+ connect(timer, &QChronoTimer::timeout, this, &MyWidget::processOneThing);
+ timer->start();
+//! [timer-interval-in-ctor]
+ }
+
+ {
+//! [timer-setinterval]
+ QChronoTimer *timer = new QChronoTimer(this);
+ connect(timer, &QChronoTimer::timeout, this, &MyWidget::processOneThing);
+ timer->setInterval(1s);
+ timer->start();
+//! [timer-setinterval]
+ }
+ }
+
+public Q_SLOTS:
+ void processOneThing();
+};
+
+int main()
+{
}
diff --git a/src/corelib/kernel/qchronotimer.cpp b/src/corelib/kernel/qchronotimer.cpp
index cbeeb97934..d67d7ca48f 100644
--- a/src/corelib/kernel/qchronotimer.cpp
+++ b/src/corelib/kernel/qchronotimer.cpp
@@ -2,7 +2,7 @@
// Copyright (C) 2016 Intel Corporation.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-#include "qtimer.h"
+#include "qchronotimer.h"
#include "qtimer_p.h"
#include "qsingleshottimer_p.h"
@@ -20,123 +20,125 @@ using namespace std::chrono_literals;
QT_BEGIN_NAMESPACE
/*!
- \class QTimer
+ \class QChronoTimer
\inmodule QtCore
- \brief The QTimer class provides repetitive and single-shot timers.
-
+ \since 6.8
\ingroup events
+ \brief The QChronoTimer class provides repetitive and single-shot timers.
- The QTimer class provides a high-level programming interface for
- timers. To use it, create a QTimer, connect its timeout() signal
- to the appropriate slots, and call start(). From then on, it will
- emit the timeout() signal at constant intervals.
-
- Example for a one second (1000 millisecond) timer (from the
- \l{widgets/analogclock}{Analog Clock} example):
+ The QChronoTimer class provides a high-level programming interface for
+ timers. To use it, create a QChronoTimer, either passing the interval to the
+ constructor, or setting it after construction using setInterval(), connect
+ its timeout() signal to the appropriate slots, and call start(). From then
+ on, it will emit the timeout() signal at constant intervals. For example:
- \snippet ../widgets/widgets/analogclock/analogclock.cpp 4
- \snippet ../widgets/widgets/analogclock/analogclock.cpp 5
- \snippet ../widgets/widgets/analogclock/analogclock.cpp 6
+ \snippet timers/timers.cpp timer-interval-in-ctor
+ \snippet timers/timers.cpp timer-setinterval
- From then on, the \c update() slot is called every second.
+ You can set a timer to time out only once by calling setSingleShot(true).
- You can set a timer to time out only once by calling
- setSingleShot(true). You can also use the static
- QTimer::singleShot() function to call a slot after a specified
- interval:
+ QChronoTimer also has singleShot() static methods:
- \snippet timers/timers.cpp 3
+ \snippet timers/timers.cpp qchronotimer-singleshot
- In multithreaded applications, you can use QTimer in any thread
+ In multithreaded applications, you can use QChronoTimer in any thread
that has an event loop. To start an event loop from a non-GUI
thread, use QThread::exec(). Qt uses the timer's
\l{QObject::thread()}{thread affinity} to determine which thread
- will emit the \l{QTimer::}{timeout()} signal. Because of this, you
+ will emit the \l{QChronoTimer::}{timeout()} signal. Because of this, you
must start and stop the timer in its thread; it is not possible to
start a timer from another thread.
- As a special case, a QTimer with a timeout of 0 will time out as soon as
- possible, though the ordering between zero timers and other sources of
- events is unspecified. Zero timers can be used to do some work while still
- providing a snappy user interface:
+ As a special case, a QChronoTimer with a timeout of \c 0ns will time out
+ as soon as possible, though the ordering between zero timers and other
+ sources of events is unspecified. Zero timers can be used to do some
+ work while still providing a responsive user interface:
- \snippet timers/timers.cpp 4
- \snippet timers/timers.cpp 5
- \snippet timers/timers.cpp 6
+ \snippet timers/timers.cpp zero-timer
- From then on, \c processOneThing() will be called repeatedly. It
- should be written in such a way that it always returns quickly
- (typically after processing one data item) so that Qt can deliver
- events to the user interface and stop the timer as soon as it has done all
- its work. This is the traditional way of implementing heavy work
- in GUI applications, but as multithreading is nowadays becoming available on
- more and more platforms, we expect that zero-millisecond
- QTimer objects will gradually be replaced by \l{QThread}s.
+ From then on, \c processOneThing() will be called repeatedly. It should
+ be written in such a way that it always returns quickly (for example,
+ after processing one data item) so that Qt can deliver events to the user
+ interface and stop the timer as soon as it has done all its work. This
+ is the traditional way of implementing heavy work in GUI applications,
+ but as multithreading is becoming available on more platforms, a modern
+ alternative is doing the heavy work in a thread other than the GUI (main)
+ thread. Qt has the QThread class, which can be used to achieve that.
\section1 Accuracy and Timer Resolution
- The accuracy of timers depends on the underlying operating system
- and hardware. Most platforms support a resolution of 1 millisecond,
- though the accuracy of the timer will not equal this resolution
- in many real-world situations.
+ The accuracy of timers depends on the underlying operating system and
+ hardware. Most platforms support requesting nano-second precision for
+ timers (for example, libc's \c nanosleep), though the accuracy of the
+ timer will not equal this resolution in many real-world situations.
- The accuracy also depends on the \l{Qt::TimerType}{timer type}. For
- Qt::PreciseTimer, QTimer will try to keep the accuracy at 1 millisecond.
- Precise timers will also never time out earlier than expected.
+ You can set the \l{Qt::TimerType}{timer type} to tell QChronoTimer which
+ precision to request from the system.
- For Qt::CoarseTimer and Qt::VeryCoarseTimer types, QTimer may wake up
- earlier than expected, within the margins for those types: 5% of the
- interval for Qt::CoarseTimer and 500 ms for Qt::VeryCoarseTimer.
+ For Qt::PreciseTimer, QChronoTimer will try to keep the precision at
+ \c 1ns. Precise timers will never time out earlier than expected.
+
+ For Qt::CoarseTimer and Qt::VeryCoarseTimer types, QChronoTimer may wake
+ up earlier than expected, within the margins for those types:
+ \list
+ \li 5% of the interval for Qt::CoarseTimer
+ \li \c 500ms for Qt::VeryCoarseTimer
+ \endlist
All timer types may time out later than expected if the system is busy or
unable to provide the requested accuracy. In such a case of timeout
overrun, Qt will emit timeout() only once, even if multiple timeouts have
expired, and then will resume the original interval.
- \section1 Alternatives to QTimer
+ \section1 Alternatives to QChronoTimer
- An alternative to using QTimer is to call QObject::startTimer()
- for your object and reimplement the QObject::timerEvent() event
- handler in your class (which must inherit QObject). The
- disadvantage is that timerEvent() does not support such
- high-level features as single-shot timers or signals.
+ An alternative to using QChronoTimer is to call QObject::startTimer()
+ for your object and reimplement the QObject::timerEvent() event handler
+ in your class (which must be a sub-class of QObject). The disadvantage
+ is that timerEvent() does not support such high-level features as
+ single-shot timers or signals.
- Another alternative is QBasicTimer. It is typically less
- cumbersome than using QObject::startTimer()
- directly. See \l{Timers} for an overview of all three approaches.
+ Another alternative is QBasicTimer. It is typically less cumbersome
+ than using QObject::startTimer() directly. See \l{Timers} for an
+ overview of all three approaches.
- Some operating systems limit the number of timers that may be
- used; Qt tries to work around these limitations.
+ Some operating systems limit the number of timers that may be used;
+ Qt does its best to work around these limitations.
\sa QBasicTimer, QTimerEvent, QObject::timerEvent(), Timers,
{Analog Clock}
*/
/*!
- Constructs a timer with the given \a parent.
+ Constructs a timer with the given \a parent, using the default interval,
+ \c 0ns.
*/
-
-QTimer::QTimer(QObject *parent)
- : QObject(*new QTimerPrivate(this), parent)
+QChronoTimer::QChronoTimer(QObject *parent)
+ : QChronoTimer(0ns, parent)
{
- Q_ASSERT(d_func()->isQTimer);
}
+/*!
+ Constructs a timer with the given \a parent, using an interval of \a nsec.
+*/
+QChronoTimer::QChronoTimer(std::chrono::nanoseconds nsec, QObject *parent)
+ : QObject(*new QTimerPrivate(nsec, this), parent)
+{
+ Q_ASSERT(!d_func()->isQTimer);
+}
/*!
Destroys the timer.
*/
-
-QTimer::~QTimer()
+QChronoTimer::~QChronoTimer()
{
if (d_func()->id != QTimerPrivate::INV_TIMER) // stop running timer
stop();
}
-
/*!
- \fn void QTimer::timeout()
+ \fn void QChronoTimer::timeout()
This signal is emitted when the timer times out.
@@ -144,56 +146,50 @@ QTimer::~QTimer()
*/
/*!
- \property QTimer::active
- \since 4.3
+ \property QChronoTimer::active
This boolean property is \c true if the timer is running; otherwise
- false.
+ \c false.
*/
/*!
- \fn bool QTimer::isActive() const
-
Returns \c true if the timer is running (pending); otherwise returns
false.
*/
-bool QTimer::isActive() const
+bool QChronoTimer::isActive() const
{
return d_func()->isActiveData.value();
}
-QBindable<bool> QTimer::bindableActive()
+QBindable<bool> QChronoTimer::bindableActive()
{
return QBindable<bool>(&d_func()->isActiveData);
}
/*!
- \fn int QTimer::timerId() const
-
Returns the ID of the timer if the timer is running; otherwise returns
-1.
*/
-int QTimer::timerId() const
+int QChronoTimer::id() const
{
return d_func()->id;
}
-
/*! \overload start()
Starts or restarts the timer with the timeout specified in \l interval.
If the timer is already running, it will be
- \l{QTimer::stop()}{stopped} and restarted.
+ \l{QChronoTimer::stop()}{stopped} and restarted.
If \l singleShot is true, the timer will be activated only once.
*/
-void QTimer::start()
+void QChronoTimer::start()
{
- Q_D(QTimer);
+ auto *d = d_func();
if (d->id != QTimerPrivate::INV_TIMER) // stop running timer
stop();
- const int id = QObject::startTimer(std::chrono::milliseconds{d->inter}, d->type);
+ const auto id = QObject::startTimer(d->intervalDuration, d->type);
if (id > 0) {
d->id = id;
d->isActiveData.notify();
@@ -201,52 +197,13 @@ void QTimer::start()
}
/*!
- Starts or restarts the timer with a timeout interval of \a msec
- milliseconds.
-
- If the timer is already running, it will be
- \l{QTimer::stop()}{stopped} and restarted.
-
- If \l singleShot is true, the timer will be activated only once. This is
- equivalent to:
-
- \code
- timer.setInterval(msec);
- timer.start();
- \endcode
-
- \note Keeping the event loop busy with a zero-timer is bound to
- cause trouble and highly erratic behavior of the UI.
-*/
-void QTimer::start(int msec)
-{
- start(msec * 1ms);
-}
-
-void QTimer::start(std::chrono::milliseconds interval)
-{
- Q_D(QTimer);
- // This could be narrowing as the interval is stored in an `int` QProperty,
- // and the type can't be changed in Qt6.
- const int msec = interval.count();
- const bool intervalChanged = msec != d->inter;
- d->inter.setValue(msec);
- start();
- if (intervalChanged)
- d->inter.notify();
-}
-
-
-
-/*!
Stops the timer.
\sa start()
*/
-
-void QTimer::stop()
+void QChronoTimer::stop()
{
- Q_D(QTimer);
+ auto *d = d_func();
if (d->id != QTimerPrivate::INV_TIMER) {
QObject::killTimer(d->id);
d->id = QTimerPrivate::INV_TIMER;
@@ -254,325 +211,84 @@ void QTimer::stop()
}
}
-
/*!
\reimp
*/
-void QTimer::timerEvent(QTimerEvent *e)
+void QChronoTimer::timerEvent(QTimerEvent *e)
{
- Q_D(QTimer);
+ auto *d = d_func();
if (e->timerId() == d->id) {
if (d->single)
stop();
- emit timeout(QPrivateSignal());
+ Q_EMIT timeout(QPrivateSignal());
}
}
/*!
- \internal
-
- Implementation of the template version of singleShot
-
- \a msec is the timer interval
- \a timerType is the timer type
- \a receiver is the receiver object, can be null. In such a case, it will be the same
- as the final sender class.
- \a slotObj the slot object
-*/
-void QTimer::singleShotImpl(std::chrono::milliseconds msec, Qt::TimerType timerType,
- const QObject *receiver,
- QtPrivate::QSlotObjectBase *slotObj)
-{
- if (msec == 0ms) {
- bool deleteReceiver = false;
- // Optimize: set a receiver context when none is given, such that we can use
- // QMetaObject::invokeMethod which is more efficient than going through a timer.
- // We need a QObject living in the current thread. But the QThread itself lives
- // in a different thread - with the exception of the main QThread which lives in
- // itself. And QThread::currentThread() is among the few QObjects we know that will
- // most certainly be there. Note that one can actually call singleShot before the
- // QApplication is created!
- if (!receiver && QThread::currentThread() == QCoreApplicationPrivate::mainThread()) {
- // reuse main thread as context object
- receiver = QThread::currentThread();
- } else if (!receiver) {
- // Create a receiver context object on-demand. According to the benchmarks,
- // this is still more efficient than going through a timer.
- receiver = new QObject;
- deleteReceiver = true;
- }
-
- auto h = QtPrivate::invokeMethodHelper({});
- QMetaObject::invokeMethodImpl(const_cast<QObject *>(receiver), slotObj,
- Qt::QueuedConnection, h.parameterCount(), h.parameters.data(), h.typeNames.data(),
- h.metaTypes.data());
-
- if (deleteReceiver)
- const_cast<QObject *>(receiver)->deleteLater();
- return;
- }
-
- new QSingleShotTimer(msec, timerType, receiver, slotObj);
-}
-
-/*!
- \fn void QTimer::singleShot(int msec, const QObject *receiver, const char *member)
- \reentrant
- \deprecated [6.8] Use the chrono overloads.
- This static function calls a slot after a given time interval.
-
- It is very convenient to use this function because you do not need
- to bother with a \l{QObject::timerEvent()}{timerEvent} or
- create a local QTimer object.
-
- Example:
- \snippet code/src_corelib_kernel_qtimer.cpp 0
-
- This sample program automatically terminates after 10 minutes
- (600,000 milliseconds).
-
- The \a receiver is the receiving object and the \a member is the
- slot. The time interval is \a msec milliseconds.
-
- \sa start()
-*/
-
-/*!
- \fn void QTimer::singleShot(int msec, Qt::TimerType timerType, const QObject *receiver, const char *member)
- \overload
- \reentrant
- \deprecated [6.8] Use the chrono overloads.
- This static function calls a slot after a given time interval.
-
- It is very convenient to use this function because you do not need
- to bother with a \l{QObject::timerEvent()}{timerEvent} or
- create a local QTimer object.
-
- The \a receiver is the receiving object and the \a member is the slot. The
- time interval is \a msec milliseconds. The \a timerType affects the
- accuracy of the timer.
-
- \sa start()
-*/
-
-void QTimer::singleShot(std::chrono::milliseconds msec, Qt::TimerType timerType,
- const QObject *receiver, const char *member)
-{
- if (Q_UNLIKELY(msec < 0ms)) {
- qWarning("QTimer::singleShot: Timers cannot have negative timeouts");
- return;
- }
- if (receiver && member) {
- if (msec == 0ms) {
- // special code shortpath for 0-timers
- const char* bracketPosition = strchr(member, '(');
- if (!bracketPosition || !(member[0] >= '0' && member[0] <= '2')) {
- qWarning("QTimer::singleShot: Invalid slot specification");
- return;
- }
- const auto methodName = QByteArrayView(member + 1, // extract method name
- bracketPosition - 1 - member).trimmed();
- QMetaObject::invokeMethod(const_cast<QObject *>(receiver), methodName.toByteArray().constData(),
- Qt::QueuedConnection);
- return;
- }
- (void) new QSingleShotTimer(msec, timerType, receiver, member);
- }
-}
-
-/*! \fn template<typename Duration, typename Functor> void QTimer::singleShot(Duration msec, const QObject *context, Functor &&functor)
- \fn template<typename Duration, typename Functor> void QTimer::singleShot(Duration msec, Qt::TimerType timerType, const QObject *context, Functor &&functor)
- \fn template<typename Duration, typename Functor> void QTimer::singleShot(Duration msec, Functor &&functor)
- \fn template<typename Duration, typename Functor> void QTimer::singleShot(Duration msec, Qt::TimerType timerType, Functor &&functor)
- \since 5.4
-
- \reentrant
- This static function calls \a functor after \a msec milliseconds.
-
- It is very convenient to use this function because you do not need
- to bother with a \l{QObject::timerEvent()}{timerEvent} or
- create a local QTimer object.
-
- If \a context is specified, then the \a functor will be called only if the
- \a context object has not been destroyed before the interval occurs. The functor
- will then be run the thread of \a context. The context's thread must have a
- running Qt event loop.
-
- If \a functor is a member
- function of \a context, then the function will be called on the object.
-
- The \a msec parameter can be an \c int or a \c std::chrono::milliseconds value.
-
- \sa start()
-*/
-
-/*!
- \fn void QTimer::singleShot(std::chrono::milliseconds msec, const QObject *receiver, const char *member)
- \since 5.8
- \overload
- \reentrant
-
- This static function calls a slot after a given time interval.
-
- It is very convenient to use this function because you do not need
- to bother with a \l{QObject::timerEvent()}{timerEvent} or
- create a local QTimer object.
-
- The \a receiver is the receiving object and the \a member is the slot. The
- time interval is given in the duration object \a msec.
-
- \sa start()
-*/
-
-/*!
- \fn void QTimer::singleShot(std::chrono::milliseconds msec, Qt::TimerType timerType, const QObject *receiver, const char *member)
- \since 5.8
- \overload
- \reentrant
-
- This static function calls a slot after a given time interval.
-
- It is very convenient to use this function because you do not need
- to bother with a \l{QObject::timerEvent()}{timerEvent} or
- create a local QTimer object.
-
- The \a receiver is the receiving object and the \a member is the slot. The
- time interval is given in the duration object \a msec. The \a timerType affects the
- accuracy of the timer.
-
- \sa start()
-*/
-
-/*!
- \fn template <typename Functor> QMetaObject::Connection QTimer::callOnTimeout(Functor &&slot)
- \since 5.12
-
- Creates a connection from the timer's timeout() signal to \a slot.
- Returns a handle to the connection.
-
- This method is provided for convenience. It's equivalent to calling:
- \code
- QObject::connect(timer, &QTimer::timeout, timer, slot, Qt::DirectConnection);
- \endcode
-
- \note This overload is not available when \c {QT_NO_CONTEXTLESS_CONNECT} is
- defined, instead use the callOnTimeout() overload that takes a context object.
-
- \sa QObject::connect(), timeout()
-*/
-
-/*!
- \fn template <typename Functor> QMetaObject::Connection QTimer::callOnTimeout(const QObject *context, Functor &&slot, Qt::ConnectionType connectionType = Qt::AutoConnection)
- \since 5.12
+ \fn template <typename Functor> QMetaObject::Connection QChronoTimer::callOnTimeout(const QObject *context, Functor &&slot, Qt::ConnectionType connectionType = Qt::AutoConnection)
\overload callOnTimeout()
- Creates a connection from the timeout() signal to \a slot to be placed in a specific
- event loop of \a context, and returns a handle to the connection.
+ Creates a connection from the timeout() signal to \a slot to be placed in a
+ specific event loop of \a context, with connection type \a connectionType,
+ and returns a handle to the connection.
- This method is provided for convenience. It's equivalent to calling:
+ This method is provided as a convenience. It's equivalent to calling:
\code
- QObject::connect(timer, &QTimer::timeout, context, slot, connectionType);
+ QObject::connect(timer, &QChronoTimer::timeout, context, slot, connectionType);
\endcode
\sa QObject::connect(), timeout()
*/
/*!
- \fn void QTimer::start(std::chrono::milliseconds msec)
- \since 5.8
- \overload
-
- Starts or restarts the timer with a timeout of duration \a msec milliseconds.
+ \property QChronoTimer::singleShot
+ \brief Whether the timer is a single-shot timer
- If the timer is already running, it will be
- \l{QTimer::stop()}{stopped} and restarted.
-
- If \l singleShot is true, the timer will be activated only once. This is
- equivalent to:
-
- \code
- timer.setInterval(msec);
- timer.start();
- \endcode
-*/
-
-/*!
- \fn std::chrono::milliseconds QTimer::intervalAsDuration() const
- \since 5.8
-
- Returns the interval of this timer as a \c std::chrono::milliseconds object.
-
- \sa interval
-*/
-
-/*!
- \fn std::chrono::milliseconds QTimer::remainingTimeAsDuration() const
- \since 5.8
-
- Returns the time remaining in this timer object as a \c
- std::chrono::milliseconds object. If this timer is due or overdue, the
- returned value is \c std::chrono::milliseconds::zero(). If the remaining
- time could not be found or the timer is not active, this function returns a
- negative duration.
-
- \sa remainingTime()
-*/
-
-/*!
- \property QTimer::singleShot
- \brief whether the timer is a single-shot timer
-
- A single-shot timer fires only once, non-single-shot timers fire
- every \l interval milliseconds.
+ A single-shot timer fires only once, non-single-shot timers fire every
+ \l interval.
The default value for this property is \c false.
\sa interval, singleShot()
*/
-void QTimer::setSingleShot(bool singleShot)
+void QChronoTimer::setSingleShot(bool singleShot)
{
d_func()->single = singleShot;
}
-bool QTimer::isSingleShot() const
+bool QChronoTimer::isSingleShot() const
{
return d_func()->single;
}
-QBindable<bool> QTimer::bindableSingleShot()
+QBindable<bool> QChronoTimer::bindableSingleShot()
{
return QBindable<bool>(&d_func()->single);
}
/*!
- \property QTimer::interval
- \brief the timeout interval in milliseconds
+ \property QChronoTimer::interval
+ \brief The timeout interval
- The default value for this property is 0. A QTimer with a timeout
- interval of 0 will time out as soon as all the events in the window
- system's event queue have been processed.
+ The default value for this property is \c 0ns.
- Setting the interval of an active timer changes its timerId().
+ A QChronoTimer with a timeout of \c 0ns will time out as soon as all
+ the events in the window system's event queue have been processed.
+
+ Setting the interval of an active timer changes the interval and acquires
+ a new id(). If the timer is not active, only the interval is changed.
\sa singleShot
*/
-void QTimer::setInterval(int msec)
-{
- setInterval(std::chrono::milliseconds{msec});
-}
-
-void QTimer::setInterval(std::chrono::milliseconds interval)
+void QChronoTimer::setInterval(std::chrono::nanoseconds nsec)
{
- Q_D(QTimer);
- // This could be narrowing as the interval is stored in an `int` QProperty,
- // and the type can't be changed in Qt6.
- const int msec = interval.count();
- d->inter.removeBindingUnlessInWrapper();
- const bool intervalChanged = msec != d->inter.valueBypassingBindings();
- d->inter.setValueBypassingBindings(msec);
- if (d->id != QTimerPrivate::INV_TIMER) { // create new timer
- QObject::killTimer(d->id); // restart timer
- const int id = QObject::startTimer(std::chrono::milliseconds{msec}, d->type);
+ auto *d = d_func();
+ d->intervalDuration.removeBindingUnlessInWrapper();
+ const bool intervalChanged = nsec != d->intervalDuration.valueBypassingBindings();
+ d->intervalDuration.setValueBypassingBindings(nsec);
+ if (d->id != QTimerPrivate::INV_TIMER) { // Create new timer
+ QObject::killTimer(d->id); // Restart timer
+ const auto id = QObject::startTimer(nsec, d->type);
if (id > 0) {
// Restarted successfully. No need to update the active state.
d->id = id;
@@ -584,63 +300,150 @@ void QTimer::setInterval(std::chrono::milliseconds interval)
}
}
if (intervalChanged)
- d->inter.notify();
+ d->intervalDuration.notify();
}
-int QTimer::interval() const
+std::chrono::nanoseconds QChronoTimer::interval() const
{
- return d_func()->inter;
+ return d_func()->intervalDuration.value();
}
-QBindable<int> QTimer::bindableInterval()
+QBindable<std::chrono::nanoseconds> QChronoTimer::bindableInterval()
{
- return QBindable<int>(&d_func()->inter);
+ return {&d_func()->intervalDuration};
}
/*!
- \property QTimer::remainingTime
- \since 5.0
- \brief the remaining time in milliseconds
+ \property QChronoTimer::remainingTime
+ \brief The remaining time
+
+ Returns the remaining duration until the timeout.
- Returns the timer's remaining value in milliseconds left until the timeout.
- If the timer is inactive, the returned value will be -1. If the timer is
- overdue, the returned value will be 0.
+ If the timer is inactive, the returned duration will be negative.
+
+ If the timer is overdue, the returned duration will be \c 0ns.
\sa interval
*/
-int QTimer::remainingTime() const
+std::chrono::nanoseconds QChronoTimer::remainingTime() const
{
- Q_D(const QTimer);
- if (d->id != QTimerPrivate::INV_TIMER) {
- return QAbstractEventDispatcher::instance()->remainingTime(d->id);
- }
-
- return -1;
+ if (isActive())
+ return QAbstractEventDispatcher::instance()->remainingTime(d_func()->id) * 1ms;
+ return std::chrono::nanoseconds::min();
}
/*!
- \property QTimer::timerType
- \brief controls the accuracy of the timer
+ \property QChronoTimer::timerType
+ \brief Controls the accuracy of the timer
The default value for this property is \c Qt::CoarseTimer.
\sa Qt::TimerType
*/
-void QTimer::setTimerType(Qt::TimerType atype)
+void QChronoTimer::setTimerType(Qt::TimerType atype)
{
d_func()->type = atype;
}
-Qt::TimerType QTimer::timerType() const
+Qt::TimerType QChronoTimer::timerType() const
{
return d_func()->type;
}
-QBindable<Qt::TimerType> QTimer::bindableTimerType()
+QBindable<Qt::TimerType> QChronoTimer::bindableTimerType()
+{
+ return {&d_func()->type};
+}
+
+/*!
+ \overload
+ \reentrant
+
+ This static function calls the slot \a member, on object \a receiver, after
+ time interval \a interval. \a timerType affects the precision of the timer
+
+ \a member has to be a member function of \a receiver; you need to use the
+ \c SLOT() macro to get this parameter.
+
+ This function is provided as a convenience to save the need to use a
+ \l{QObject::timerEvent()}{timerEvent} or create a local QTimer object.
+
+ \sa start(), Qt::TimerType
+*/
+void QChronoTimer::singleShot(std::chrono::nanoseconds interval, Qt::TimerType timerType,
+ const QObject *receiver, const char *member)
{
- return QBindable<Qt::TimerType>(&d_func()->type);
+ if (Q_UNLIKELY(interval < 0ns)) {
+ qWarning("QChronoTimer::singleShot: Timers cannot have negative timeouts");
+ return;
+ }
+ if (receiver && member) {
+ if (interval == 0ns) {
+ // special code shortpath for 0-timers
+ const char* bracketPosition = strchr(member, '(');
+ if (!bracketPosition || !(member[0] >= '0' && member[0] <= '2')) {
+ qWarning("QChronoTimer::singleShot: Invalid slot specification");
+ return;
+ }
+ const auto methodName = QByteArrayView(member + 1, // extract method name
+ bracketPosition - 1 - member).trimmed();
+ QMetaObject::invokeMethod(const_cast<QObject *>(receiver),
+ methodName.toByteArray().constData(),
+ Qt::QueuedConnection);
+ return;
+ }
+ (void) new QSingleShotTimer(interval, timerType, receiver, member);
+ }
+}
+
+/*!
+ \internal
+
+ \list
+ \li \a interval the time interval
+ \li \a timerType the type of the timer; this affects the precision of
+ the timer
+ \li \a receiver the receiver or context object; if this is \c nullptr,
+ this method will figure out a context object to use, see code
+ comments below
+ \li \a slotObj a callable, for example a lambda
+ \endlist
+*/
+void QChronoTimer::singleShotImpl(std::chrono::nanoseconds interval, Qt::TimerType timerType,
+ const QObject *receiver, QtPrivate::QSlotObjectBase *slotObj)
+{
+ if (interval == 0ns) {
+ bool deleteReceiver = false;
+ // Optimize: set a receiver context when none is given, such that we can use
+ // QMetaObject::invokeMethod which is more efficient than going through a timer.
+ // We need a QObject living in the current thread. But the QThread itself lives
+ // in a different thread - with the exception of the main QThread which lives in
+ // itself. And QThread::currentThread() is among the few QObjects we know that will
+ // most certainly be there. Note that one can actually call singleShot before the
+ // QApplication is created!
+ if (!receiver && QThread::currentThread() == QCoreApplicationPrivate::mainThread()) {
+ // reuse main thread as context object
+ receiver = QThread::currentThread();
+ } else if (!receiver) {
+ // Create a receiver context object on-demand. According to the benchmarks,
+ // this is still more efficient than going through a timer.
+ receiver = new QObject;
+ deleteReceiver = true;
+ }
+
+ auto h = QtPrivate::invokeMethodHelper({});
+ QMetaObject::invokeMethodImpl(const_cast<QObject *>(receiver), slotObj,
+ Qt::QueuedConnection, h.parameterCount(), h.parameters.data(), h.typeNames.data(),
+ h.metaTypes.data());
+
+ if (deleteReceiver)
+ const_cast<QObject *>(receiver)->deleteLater();
+ return;
+ }
+
+ new QSingleShotTimer(interval, timerType, receiver, slotObj);
}
QT_END_NAMESPACE
-#include "moc_qtimer.cpp"
+#include "moc_qchronotimer.cpp"
diff --git a/src/corelib/kernel/qchronotimer.h b/src/corelib/kernel/qchronotimer.h
index 9b59895e60..58735c46f2 100644
--- a/src/corelib/kernel/qchronotimer.h
+++ b/src/corelib/kernel/qchronotimer.h
@@ -1,42 +1,50 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-#ifndef QTIMER_H
-#define QTIMER_H
-
-#include <QtCore/qglobal.h>
+#ifndef QCHRONOTIMER_H
+#define QCHRONOTIMER_H
#ifndef QT_NO_QOBJECT
-#include <QtCore/qbasictimer.h> // conceptual inheritance
+#include <QtCore/qcoreevent.h>
+#include <QtCore/qnamespace.h>
#include <QtCore/qobject.h>
+#include <QtCore/qproperty.h>
#include <chrono>
QT_BEGIN_NAMESPACE
class QTimerPrivate;
-class Q_CORE_EXPORT QTimer : public QObject
+class Q_CORE_EXPORT QChronoTimer : public QObject
{
Q_OBJECT
- Q_PROPERTY(bool singleShot READ isSingleShot WRITE setSingleShot BINDABLE bindableSingleShot)
- Q_PROPERTY(int interval READ interval WRITE setInterval BINDABLE bindableInterval)
- Q_PROPERTY(int remainingTime READ remainingTime)
- Q_PROPERTY(Qt::TimerType timerType READ timerType WRITE setTimerType BINDABLE bindableTimerType)
- Q_PROPERTY(bool active READ isActive STORED false BINDABLE bindableActive)
+ Q_PROPERTY(bool singleShot READ isSingleShot WRITE setSingleShot
+ BINDABLE bindableSingleShot FINAL)
+ Q_PROPERTY(std::chrono::nanoseconds interval READ interval WRITE setInterval
+ BINDABLE bindableInterval FINAL)
+ Q_PROPERTY(std::chrono::nanoseconds remainingTime READ remainingTime FINAL)
+ Q_PROPERTY(Qt::TimerType timerType READ timerType WRITE setTimerType
+ BINDABLE bindableTimerType FINAL)
+ Q_PROPERTY(bool active READ isActive STORED false BINDABLE bindableActive FINAL)
+
+ template <typename Functor>
+ using FunctorContext = typename QtPrivate::ContextTypeForFunctor<Functor>::ContextType;
+
public:
- explicit QTimer(QObject *parent = nullptr);
- ~QTimer();
+ explicit QChronoTimer(std::chrono::nanoseconds nsec, QObject *parent = nullptr);
+ explicit QChronoTimer(QObject *parent = nullptr);
+ ~QChronoTimer() override;
bool isActive() const;
QBindable<bool> bindableActive();
- int timerId() const;
+ int id() const;
- void setInterval(int msec);
- int interval() const;
- QBindable<int> bindableInterval();
+ void setInterval(std::chrono::nanoseconds nsec);
+ std::chrono::nanoseconds interval() const;
+ QBindable<std::chrono::nanoseconds> bindableInterval();
- int remainingTime() const;
+ std::chrono::nanoseconds remainingTime() const;
void setTimerType(Qt::TimerType atype);
Qt::TimerType timerType() const;
@@ -46,142 +54,96 @@ public:
bool isSingleShot() const;
QBindable<bool> bindableSingleShot();
- QT_CORE_INLINE_SINCE(6, 8)
- static void singleShot(int msec, const QObject *receiver, const char *member);
-
- QT_CORE_INLINE_SINCE(6, 8)
- static void singleShot(int msec, Qt::TimerType timerType, const QObject *receiver, const char *member);
-
// singleShot with context
#ifdef Q_QDOC
- template <typename Duration, typename Functor>
- static inline void singleShot(Duration interval, const QObject *receiver, Functor &&slot);
- template <typename Duration, typename Functor>
- static inline void singleShot(Duration interval, Qt::TimerType timerType,
+ template <typename Functor>
+ static inline void singleShot(std::chrono::nanoseconds interval,
+ const QObject *receiver, Functor &&slot);
+ template <typename Functor>
+ static inline void singleShot(std::chrono::nanoseconds interval interval,
+ Qt::TimerType timerType,
const QObject *receiver, Functor &&slot);
#else
- template <typename Duration, typename Functor>
- static inline void singleShot(Duration interval,
- const typename QtPrivate::ContextTypeForFunctor<Functor>::ContextType *receiver,
- Functor &&slot)
+ template <typename Functor>
+ static void singleShot(std::chrono::nanoseconds interval,
+ const FunctorContext<Functor> *receiver, Functor &&slot)
{
- singleShot(interval, defaultTypeFor(interval), receiver, std::forward<Functor>(slot));
+ singleShot(interval, defaultTimerTypeFor(interval), receiver, std::forward<Functor>(slot));
}
- template <typename Duration, typename Functor>
- static inline void singleShot(Duration interval, Qt::TimerType timerType,
- const typename QtPrivate::ContextTypeForFunctor<Functor>::ContextType *receiver,
- Functor &&slot)
+ template <typename Functor>
+ static void singleShot(std::chrono::nanoseconds interval, Qt::TimerType timerType,
+ const FunctorContext<Functor> *receiver, Functor &&slot)
{
using Prototype = void(*)();
- singleShotImpl(interval, timerType, receiver,
- QtPrivate::makeCallableObject<Prototype>(std::forward<Functor>(slot)));
+ auto *slotObj = QtPrivate::makeCallableObject<Prototype>(std::forward<Functor>(slot));
+ singleShotImpl(interval, timerType, receiver, slotObj);
}
#endif
- // singleShot without context
- template <typename Duration, typename Functor>
- static inline void singleShot(Duration interval, Functor &&slot)
- {
- singleShot(interval, defaultTypeFor(interval), nullptr, std::forward<Functor>(slot));
- }
- template <typename Duration, typename Functor>
- static inline void singleShot(Duration interval, Qt::TimerType timerType, Functor &&slot)
+ template <typename Functor>
+ static void singleShot(std::chrono::nanoseconds interval, Qt::TimerType timerType,
+ Functor &&slot)
+ { singleShot(interval, timerType, nullptr, std::forward<Functor>(slot)); }
+
+ template <typename Functor>
+ static void singleShot(std::chrono::nanoseconds interval, Functor &&slot)
{
- singleShot(interval, timerType, nullptr, std::forward<Functor>(slot));
+ singleShot(interval, defaultTimerTypeFor(interval), nullptr, std::forward<Functor>(slot));
}
+ static void singleShot(std::chrono::nanoseconds interval, Qt::TimerType timerType,
+ const QObject *receiver, const char *member);
+ static void singleShot(std::chrono::nanoseconds interval, const QObject *receiver,
+ const char *member)
+ { singleShot(interval, defaultTimerTypeFor(interval), receiver, member); }
+
#ifdef Q_QDOC
template <typename Functor>
- QMetaObject::Connection callOnTimeout(Functor &&slot);
- template <typename Functor>
- QMetaObject::Connection callOnTimeout(const QObject *context, Functor &&slot, Qt::ConnectionType connectionType = Qt::AutoConnection);
+ QMetaObject::Connection callOnTimeout(const QObject *context, Functor &&slot,
+ Qt::ConnectionType connectionType = Qt::AutoConnection);
#else
template <typename ... Args>
QMetaObject::Connection callOnTimeout(Args && ...args)
{
- return QObject::connect(this, &QTimer::timeout, std::forward<Args>(args)... );
+ return QObject::connect(this, &QChronoTimer::timeout, std::forward<Args>(args)... );
}
-
#endif
public Q_SLOTS:
- void start(int msec);
-
void start();
void stop();
Q_SIGNALS:
void timeout(QPrivateSignal);
-public:
- void setInterval(std::chrono::milliseconds value);
-
- std::chrono::milliseconds intervalAsDuration() const
- {
- return std::chrono::milliseconds(interval());
- }
-
- std::chrono::milliseconds remainingTimeAsDuration() const
- {
- return std::chrono::milliseconds(remainingTime());
- }
-
- static void singleShot(std::chrono::milliseconds value, const QObject *receiver, const char *member)
- {
- singleShot(value, defaultTypeFor(value), receiver, member);
- }
- static void singleShot(std::chrono::milliseconds interval, Qt::TimerType timerType,
- const QObject *receiver, const char *member);
-
- void start(std::chrono::milliseconds value);
-
protected:
void timerEvent(QTimerEvent *) override;
private:
- Q_DISABLE_COPY(QTimer)
- Q_DECLARE_PRIVATE(QTimer)
+ Q_DISABLE_COPY(QChronoTimer)
- inline int startTimer(int){ return -1;}
- inline void killTimer(int){}
+ // QChronoTimer uses QTimerPrivate
+ inline QTimerPrivate *d_func() noexcept
+ { Q_CAST_IGNORE_ALIGN(return reinterpret_cast<QTimerPrivate *>(qGetPtrHelper(d_ptr));) }
+ inline const QTimerPrivate *d_func() const noexcept
+ { Q_CAST_IGNORE_ALIGN(return reinterpret_cast<const QTimerPrivate *>(qGetPtrHelper(d_ptr));) }
- static constexpr Qt::TimerType defaultTypeFor(int msecs) noexcept
- { return defaultTypeFor(std::chrono::milliseconds{msecs}); }
+ // These two functions are inherited from QObject
+ int startTimer(std::chrono::nanoseconds) = delete;
+ void killTimer(int) = delete;
- static constexpr Qt::TimerType defaultTypeFor(std::chrono::milliseconds interval) noexcept
+ static constexpr Qt::TimerType defaultTimerTypeFor(std::chrono::nanoseconds interval) noexcept
{
- // coarse timers are worst in their first firing
- // so we prefer a high precision timer for something that happens only once
- // unless the timeout is too big, in which case we go for coarse anyway
using namespace std::chrono_literals;
return interval >= 2s ? Qt::CoarseTimer : Qt::PreciseTimer;
}
- QT_CORE_INLINE_SINCE(6, 8)
- static void singleShotImpl(int msec, Qt::TimerType timerType,
- const QObject *receiver, QtPrivate::QSlotObjectBase *slotObj);
-
- static void singleShotImpl(std::chrono::milliseconds interval, Qt::TimerType timerType,
+ static void singleShotImpl(std::chrono::nanoseconds interval, Qt::TimerType timerType,
const QObject *receiver, QtPrivate::QSlotObjectBase *slotObj);
};
-#if QT_CORE_INLINE_IMPL_SINCE(6, 8)
-void QTimer::singleShot(int msec, const QObject *receiver, const char *member)
-{ singleShot(std::chrono::milliseconds{msec}, receiver, member); }
-
-void QTimer::singleShot(int msec, Qt::TimerType timerType, const QObject *receiver,
- const char *member)
-{ singleShot(std::chrono::milliseconds{msec}, timerType, receiver, member); }
-
-void QTimer::singleShotImpl(int msec, Qt::TimerType timerType,
- const QObject *receiver, QtPrivate::QSlotObjectBase *slotObj)
-{
- singleShotImpl(std::chrono::milliseconds{msec}, timerType, receiver, slotObj);
-}
-#endif
-
QT_END_NAMESPACE
#endif // QT_NO_QOBJECT
-#endif // QTIMER_H
+#endif // QCHRONOTIMER_H
diff --git a/src/corelib/kernel/qobjectdefs.h b/src/corelib/kernel/qobjectdefs.h
index ff06d3762c..190901d5d1 100644
--- a/src/corelib/kernel/qobjectdefs.h
+++ b/src/corelib/kernel/qobjectdefs.h
@@ -639,6 +639,7 @@ private:
const void **parameters, const char **typeNames,
const QtPrivate::QMetaTypeInterface **metaTypes);
friend class QTimer;
+ friend class QChronoTimer;
};
class Q_CORE_EXPORT QMetaObject::Connection {
diff --git a/src/corelib/kernel/qtimer.cpp b/src/corelib/kernel/qtimer.cpp
index 0e09bc9565..cbeeb97934 100644
--- a/src/corelib/kernel/qtimer.cpp
+++ b/src/corelib/kernel/qtimer.cpp
@@ -118,8 +118,9 @@ QT_BEGIN_NAMESPACE
*/
QTimer::QTimer(QObject *parent)
- : QObject(*new QTimerPrivate, parent)
+ : QObject(*new QTimerPrivate(this), parent)
{
+ Q_ASSERT(d_func()->isQTimer);
}
diff --git a/src/corelib/kernel/qtimer_p.h b/src/corelib/kernel/qtimer_p.h
index 81e8a5fccb..b1e0830dcf 100644
--- a/src/corelib/kernel/qtimer_p.h
+++ b/src/corelib/kernel/qtimer_p.h
@@ -15,24 +15,58 @@
#include "qobject_p.h"
#include "qproperty_p.h"
#include "qtimer.h"
+#include "qchronotimer.h"
QT_BEGIN_NAMESPACE
class QTimerPrivate : public QObjectPrivate
{
- Q_DECLARE_PUBLIC(QTimer)
public:
+ QTimerPrivate(QTimer *qq)
+ : q(qq),
+ isQTimer(true)
+ {}
+
+ QTimerPrivate(std::chrono::nanoseconds nsec, QChronoTimer *qq)
+ : intervalDuration(nsec),
+ q(qq)
+ {
+ intervalDuration.notify();
+ }
+
static constexpr int INV_TIMER = -1; // invalid timer id
- void setInterval(int msec) { q_func()->setInterval(msec); }
+ void setIntervalDuration(std::chrono::nanoseconds nsec)
+ {
+ if (isQTimer) {
+ const auto msec = std::chrono::duration_cast<std::chrono::milliseconds>(nsec);
+ static_cast<QTimer *>(q)->setInterval(msec);
+ } else {
+ static_cast<QChronoTimer *>(q)->setInterval(nsec);
+ }
+ }
+
+ void setInterval(int msec)
+ {
+ Q_ASSERT(isQTimer);
+ static_cast<QTimer *>(q)->setInterval(msec);
+ }
+
bool isActiveActualCalculation() const { return id > 0; }
int id = INV_TIMER;
Q_OBJECT_COMPAT_PROPERTY_WITH_ARGS(QTimerPrivate, int, inter, &QTimerPrivate::setInterval, 0)
+ Q_OBJECT_COMPAT_PROPERTY_WITH_ARGS(QTimerPrivate, std::chrono::nanoseconds, intervalDuration,
+ &QTimerPrivate::setIntervalDuration,
+ std::chrono::nanoseconds{0})
Q_OBJECT_BINDABLE_PROPERTY_WITH_ARGS(QTimerPrivate, bool, single, false)
Q_OBJECT_BINDABLE_PROPERTY_WITH_ARGS(QTimerPrivate, Qt::TimerType, type, Qt::CoarseTimer)
Q_OBJECT_COMPUTED_PROPERTY(QTimerPrivate, bool, isActiveData,
&QTimerPrivate::isActiveActualCalculation)
+
+ QObject *q;
+ // true if q is a QTimer*, false otherwise
+ const bool isQTimer = false;
};
QT_END_NAMESPACE
diff --git a/tests/auto/corelib/kernel/CMakeLists.txt b/tests/auto/corelib/kernel/CMakeLists.txt
index 130fc080b6..c2feb38641 100644
--- a/tests/auto/corelib/kernel/CMakeLists.txt
+++ b/tests/auto/corelib/kernel/CMakeLists.txt
@@ -2,6 +2,7 @@
# SPDX-License-Identifier: BSD-3-Clause
add_subdirectory(qapplicationstatic)
+add_subdirectory(qchronotimer)
add_subdirectory(qcoreapplication)
add_subdirectory(qdeadlinetimer)
add_subdirectory(qelapsedtimer)
diff --git a/tests/auto/corelib/kernel/qchronotimer/.gitignore b/tests/auto/corelib/kernel/qchronotimer/.gitignore
index 14fd00629e..254f7a0281 100644
--- a/tests/auto/corelib/kernel/qchronotimer/.gitignore
+++ b/tests/auto/corelib/kernel/qchronotimer/.gitignore
@@ -1 +1 @@
-tst_qtimer
+tst_qchronotimer
diff --git a/tests/auto/corelib/kernel/qchronotimer/CMakeLists.txt b/tests/auto/corelib/kernel/qchronotimer/CMakeLists.txt
index 6bb3b15850..43164858c5 100644
--- a/tests/auto/corelib/kernel/qchronotimer/CMakeLists.txt
+++ b/tests/auto/corelib/kernel/qchronotimer/CMakeLists.txt
@@ -3,7 +3,7 @@
if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
- project(tst_qtimer LANGUAGES CXX)
+ project(tst_qchronotimer LANGUAGES CXX)
find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
endif()
@@ -11,24 +11,23 @@ if (NOT QT_FEATURE_thread)
return()
endif()
-function(addTimerTest test)
+function(addChronoTimerTest test)
qt_internal_add_test(${test}
SOURCES
- tst_qtimer.cpp
+ tst_qchronotimer.cpp
LIBRARIES
Qt::CorePrivate
Qt::TestPrivate
)
endfunction()
-addTimerTest(tst_qtimer)
+addChronoTimerTest(tst_qchronotimer)
if(QT_FEATURE_glib AND UNIX)
- addTimerTest(tst_qtimer_no_glib)
- qt_internal_extend_target(tst_qtimer_no_glib
+ addChronoTimerTest(tst_qchronotimer_no_glib)
+ qt_internal_extend_target(tst_qchronotimer_no_glib
DEFINES
DISABLE_GLIB
- tst_QTimer=tst_QTimer_no_glib # Class name in the unittest
+ tst_QChronoTimer=tst_QChronoTimer_no_glib # Class name in the unittest
)
endif()
-
diff --git a/tests/auto/corelib/kernel/qchronotimer/tst_qchronotimer.cpp b/tests/auto/corelib/kernel/qchronotimer/tst_qchronotimer.cpp
index 467fc9abd7..2b137b06e3 100644
--- a/tests/auto/corelib/kernel/qchronotimer/tst_qchronotimer.cpp
+++ b/tests/auto/corelib/kernel/qchronotimer/tst_qchronotimer.cpp
@@ -1,20 +1,10 @@
// Copyright (C) 2020 The Qt Company Ltd.
// Copyright (C) 2016 Intel Corporation.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-/* WARNING: this source-code is reused by another test.
-
- As Qt built with GUI support may use a different backend for its event loops
- and other timer-related matters, it is important to test it in that form, as
- well as in its GUI-less form. So this source file is reused by a build config
- in the GUI module. Similarly, testing with and without glib is supported,
- where relevant (see DISABLE_GLIB below).
-*/
#ifdef QT_GUI_LIB
-// When compiled as tests/auto/gui/kernel/qguitimer/'s source-code:
# include <QtGui/QGuiApplication>
#else
-// When compiled as tests/auto/corelib/kernel/qtimer/'s source-code:
# include <QtCore/QCoreApplication>
#endif
@@ -23,8 +13,10 @@
#include <QSignalSpy>
#include <QtTest/private/qpropertytesthelper_p.h>
-#include <qtimer.h>
+#include <qbasictimer.h>
+#include <qchronotimer.h>
#include <qthread.h>
+#include <qtimer.h>
#include <qelapsedtimer.h>
#include <qproperty.h>
@@ -32,6 +24,8 @@
#include <unistd.h>
#endif
+using namespace std::chrono_literals;
+
#ifdef DISABLE_GLIB
static bool glibDisabled = []() {
qputenv("QT_NO_GLIB", "1");
@@ -39,25 +33,16 @@ static bool glibDisabled = []() {
}();
#endif
-using namespace std::chrono_literals;
-
-class tst_QTimer : public QObject
+class tst_QChronoTimer : public QObject
{
Q_OBJECT
-public:
- static void initMain();
private slots:
- void cleanupTestCase();
void zeroTimer();
- void singleShotTimeout();
+ void singleShotTimeout(); // Non-static singleShot()
void timeout();
- void singleShotNormalizes_data();
- void singleShotNormalizes();
void sequentialTimers_data();
void sequentialTimers();
- void singleShotSequentialTimers_data();
- void singleShotSequentialTimers();
void remainingTime();
void remainingTimeInitial_data();
void remainingTimeInitial();
@@ -70,24 +55,19 @@ private slots:
void timerInfiniteRecursion();
void recurringTimer_data();
void recurringTimer();
- void deleteLaterOnQTimer(); // long name, don't want to shadow QObject::deleteLater()
+ void deleteLaterOnQChronoTimer(); // long name, don't want to shadow QObject::deleteLater()
void moveToThread();
void restartedTimerFiresTooSoon();
void timerFiresOnlyOncePerProcessEvents_data();
void timerFiresOnlyOncePerProcessEvents();
void timerIdPersistsAfterThreadExit();
void cancelLongTimer();
- void singleShotStaticFunctionZeroTimeout();
void recurseOnTimeoutAndStopTimer();
- void singleShotToFunctors();
- void singleShot_chrono();
- void singleShot_static();
- void crossThreadSingleShotToFunctor_data();
- void crossThreadSingleShotToFunctor();
void timerOrder();
void timerOrder_data();
void timerOrderBackgroundThread();
void timerOrderBackgroundThread_data() { timerOrder_data(); }
+ void timerPrecision();
void dontBlockEvents();
void postedEventsShouldNotStarveTimers();
@@ -100,15 +80,15 @@ private slots:
void negativeInterval();
};
-void tst_QTimer::zeroTimer()
+void tst_QChronoTimer::zeroTimer()
{
- QTimer timer;
+ QChronoTimer timer;
QVERIFY(!timer.isSingleShot());
- timer.setInterval(0);
+ timer.setInterval(0ns);
timer.setSingleShot(true);
QVERIFY(timer.isSingleShot());
- QSignalSpy timeoutSpy(&timer, &QTimer::timeout);
+ QSignalSpy timeoutSpy(&timer, &QChronoTimer::timeout);
timer.start();
// Pass timeout to work round glib issue, see QTBUG-84291.
@@ -117,135 +97,80 @@ void tst_QTimer::zeroTimer()
QCOMPARE(timeoutSpy.size(), 1);
}
-void tst_QTimer::singleShotTimeout()
+void tst_QChronoTimer::singleShotTimeout()
{
- QTimer timer;
+ QChronoTimer timer;
QVERIFY(!timer.isSingleShot());
timer.setSingleShot(true);
QVERIFY(timer.isSingleShot());
- QSignalSpy timeoutSpy(&timer, &QTimer::timeout);
- timer.start(100);
+ QSignalSpy timeoutSpy(&timer, &QChronoTimer::timeout);
+ timer.setInterval(100ms);
+ timer.start();
- QVERIFY(timeoutSpy.wait(500));
+ QVERIFY(timeoutSpy.wait(500ms));
QCOMPARE(timeoutSpy.size(), 1);
- QTest::qWait(500);
+ QTest::qWait(500ms);
QCOMPARE(timeoutSpy.size(), 1);
}
-#define TIMEOUT_TIMEOUT 200
+static constexpr auto Timeout_Interval = 200ms;
-void tst_QTimer::timeout()
+void tst_QChronoTimer::timeout()
{
- QTimer timer;
- QSignalSpy timeoutSpy(&timer, &QTimer::timeout);
- timer.start(100);
+ QChronoTimer timer{100ms};
+ QSignalSpy timeoutSpy(&timer, &QChronoTimer::timeout);
+ timer.start();
QCOMPARE(timeoutSpy.size(), 0);
- QTRY_VERIFY_WITH_TIMEOUT(timeoutSpy.size() > 0, TIMEOUT_TIMEOUT);
- int oldCount = timeoutSpy.size();
-
- QTRY_VERIFY_WITH_TIMEOUT(timeoutSpy.size() > oldCount, TIMEOUT_TIMEOUT);
-}
-
-void tst_QTimer::singleShotNormalizes_data()
-{
- QTest::addColumn<QByteArray>("slotName");
+ QTRY_VERIFY_WITH_TIMEOUT(timeoutSpy.size() > 0, Timeout_Interval);
+ const qsizetype oldCount = timeoutSpy.size();
- QTest::newRow("normalized") << QByteArray(SLOT(exitLoop()));
-
- QTest::newRow("space-before") << QByteArray(SLOT( exitLoop()));
- QTest::newRow("space-after") << QByteArray(SLOT(exitLoop ()));
- QTest::newRow("space-around") << QByteArray(SLOT( exitLoop ()));
- QTest::newRow("spaces-before") << QByteArray(SLOT( exitLoop()));
- QTest::newRow("spaces-after") << QByteArray(SLOT(exitLoop ()));
- QTest::newRow("spaces-around") << QByteArray(SLOT( exitLoop ()));
-
- QTest::newRow("space-in-parens") << QByteArray(SLOT(exitLoop( )));
- QTest::newRow("spaces-in-parens") << QByteArray(SLOT(exitLoop( )));
- QTest::newRow("space-after-parens") << QByteArray(SLOT(exitLoop() ));
- QTest::newRow("spaces-after-parens") << QByteArray(SLOT(exitLoop() ));
+ QTRY_VERIFY_WITH_TIMEOUT(timeoutSpy.size() > oldCount, Timeout_Interval);
}
-void tst_QTimer::singleShotNormalizes()
-{
- static constexpr auto TestTimeout = 250ms;
- QFETCH(QByteArray, slotName);
- QEventLoop loop;
-
- // control test: regular connection
- {
- QTimer timer;
- QVERIFY(QObject::connect(&timer, SIGNAL(timeout()), &QTestEventLoop::instance(), slotName));
- timer.setSingleShot(true);
- timer.start(1);
- QTestEventLoop::instance().enterLoop(TestTimeout);
- QVERIFY(!QTestEventLoop::instance().timeout());
- }
-
- // non-zero time
- QTimer::singleShot(1, &QTestEventLoop::instance(), slotName);
- QTestEventLoop::instance().enterLoop(TestTimeout);
- QVERIFY(!QTestEventLoop::instance().timeout());
-
- QTimer::singleShot(1ms, &QTestEventLoop::instance(), slotName);
- QTestEventLoop::instance().enterLoop(TestTimeout);
- QVERIFY(!QTestEventLoop::instance().timeout());
-
- // zero time
- QTimer::singleShot(0, &QTestEventLoop::instance(), slotName);
- QTestEventLoop::instance().enterLoop(TestTimeout);
- QVERIFY(!QTestEventLoop::instance().timeout());
-
- QTimer::singleShot(0ms, &QTestEventLoop::instance(), slotName);
- QTestEventLoop::instance().enterLoop(TestTimeout);
- QVERIFY(!QTestEventLoop::instance().timeout());
-}
-
-void tst_QTimer::sequentialTimers_data()
+void tst_QChronoTimer::sequentialTimers_data()
{
#ifdef Q_OS_WIN
QSKIP("The API used by QEventDispatcherWin32 doesn't respect the order");
#endif
- QTest::addColumn<QList<int>>("timeouts");
- auto addRow = [](const QList<int> &l) {
+ QTest::addColumn<QList<std::chrono::milliseconds>>("timeouts");
+ auto addRow = [](const QList<std::chrono::milliseconds> &l) {
+ Q_ASSERT_X(std::is_sorted(l.begin(), l.end()),
+ "tst_QChronoTimer", "input list must be sorted");
QByteArray name;
- int last = -1;
- for (int i = 0; i < l.size(); ++i) {
- Q_ASSERT_X(l[i] >= last, "tst_QTimer", "input list must be sorted");
- name += QByteArray::number(l[i]) + ',';
- }
+ for (auto msec : l)
+ name += QByteArray::number(msec.count()) + ',';
name.chop(1);
QTest::addRow("%s", name.constData()) << l;
};
// PreciseTimers
- addRow({0, 0, 0, 0, 0, 0});
- addRow({0, 1, 2});
- addRow({1, 1, 1, 2, 2, 2, 2});
- addRow({1, 2, 3});
- addRow({19, 19, 19});
- // CoarseTimer for setInterval
- addRow({20, 20, 20, 20, 20});
- addRow({25, 25, 25, 25, 25, 25, 50});
+ addRow({0ms, 0ms, 0ms, 0ms, 0ms, 0ms});
+ addRow({0ms, 1ms, 2ms});
+ addRow({1ms, 1ms, 1ms, 2ms, 2ms, 2ms, 2ms});
+ addRow({1ms, 2ms, 3ms});
+ addRow({19ms, 19ms, 19ms});
+ // CoarseTimer for setinterval
+ addRow({20ms, 20ms, 20ms, 20ms, 20ms});
+ addRow({25ms, 25ms, 25ms, 25ms, 25ms, 25ms, 50ms});
}
-void tst_QTimer::sequentialTimers()
+void tst_QChronoTimer::sequentialTimers()
{
- QFETCH(const QList<int>, timeouts);
+ QFETCH(const QList<std::chrono::milliseconds>, timeouts);
QByteArray result, expected;
- std::vector<std::unique_ptr<QTimer>> timers;
+ std::vector<std::unique_ptr<QChronoTimer>> timers;
expected.resize(timeouts.size());
result.reserve(timeouts.size());
timers.reserve(timeouts.size());
for (int i = 0; i < timeouts.size(); ++i) {
- auto timer = std::make_unique<QTimer>();
+ auto timer = std::make_unique<QChronoTimer>(timeouts[i]);
timer->setSingleShot(true);
- timer->setInterval(timeouts[i]);
char c = 'A' + i;
expected[i] = c;
- QObject::connect(timer.get(), &QTimer::timeout, this, [&result, c = c]() {
+ QObject::connect(timer.get(), &QChronoTimer::timeout, this, [&result, c = c]() {
result.append(c);
});
timers.push_back(std::move(timer));
@@ -255,62 +180,37 @@ void tst_QTimer::sequentialTimers()
for (auto &timer : timers)
timer->start();
- QTestEventLoop::instance().enterLoopMSecs(timeouts.last() * 2 + 10);
-
- QCOMPARE(result, expected);
-}
-
-void tst_QTimer::singleShotSequentialTimers_data()
-{
- sequentialTimers_data();
-}
-
-void tst_QTimer::singleShotSequentialTimers()
-{
- QFETCH(const QList<int>, timeouts);
- QByteArray result, expected;
- expected.resize(timeouts.size());
- result.reserve(timeouts.size());
- for (int i = 0; i < timeouts.size(); ++i) {
- char c = 'A' + i;
- expected[i] = c;
- QTimer::singleShot(timeouts[i], this, [&result, c = c]() {
- result.append(c);
- });
- }
-
- QTestEventLoop::instance().enterLoopMSecs(timeouts.last() * 2 + 10);
+ QTestEventLoop::instance().enterLoop(timeouts.last() * 2 + 10ms);
QCOMPARE(result, expected);
}
-void tst_QTimer::remainingTime()
+void tst_QChronoTimer::remainingTime()
{
- QTimer tested;
+ QChronoTimer tested;
tested.setTimerType(Qt::PreciseTimer);
- QTimer tester;
+ QChronoTimer tester;
tester.setTimerType(Qt::PreciseTimer);
tester.setSingleShot(true);
- const int testedInterval = 200;
- const int testerInterval = 50;
- const int expectedRemainingTime = testedInterval - testerInterval;
+ constexpr auto tested_interval = 200ms;
+ constexpr auto tester_interval = 50ms;
+ constexpr auto expectedRemainingTime = tested_interval - tester_interval;
int testIteration = 0;
const int desiredTestCount = 2;
- auto connection = QObject::connect(&tested, &QTimer::timeout, [&tester]() {
- // We let tested (which isn't a single-shot) run repeatedly, to verify
- // it *does* repeat, and check that the single-shot tester, starting
- // at the same time, does finish first each time, by about the right duration.
- tester.start(); // Start tester again.
- });
+ // We let tested (which isn't a single-shot) run repeatedly, to verify
+ // it *does* repeat, and check that the single-shot tester, starting
+ // at the same time, does finish first each time, by about the right duration.
+ auto connection = QObject::connect(&tested, &QChronoTimer::timeout,
+ &tester, &QChronoTimer::start);
- QObject::connect(&tester, &QTimer::timeout, [&]() {
- const int remainingTime = tested.remainingTime();
+ QObject::connect(&tester, &QChronoTimer::timeout, this, [&]() {
+ const std::chrono::nanoseconds remainingTime = tested.remainingTime();
// We expect that remainingTime is at most 150 and not overdue.
- const bool remainingTimeInRange = remainingTime > 0
+ const bool remainingTimeInRange = remainingTime > 0ns
&& remainingTime <= expectedRemainingTime;
if (remainingTimeInRange)
++testIteration;
@@ -322,145 +222,155 @@ void tst_QTimer::remainingTime()
if (testIteration == desiredTestCount)
QObject::disconnect(connection); // Last iteration, don't start tester again.
QVERIFY2(remainingTimeInRange, qPrintable("Remaining time "
- + QByteArray::number(remainingTime) + "ms outside expected range (0ms, "
- + QByteArray::number(expectedRemainingTime) + "ms]"));
+ + QByteArray::number(remainingTime.count()) + "ms outside expected range (0ns, "
+ + QByteArray::number(expectedRemainingTime.count()) + "ms]"));
});
- tested.start(testedInterval);
- tester.start(testerInterval); // Start tester for the 1st time.
+ tested.setInterval(tested_interval);
+ tested.start();
+ tester.setInterval(tester_interval);
+ tester.start(); // Start tester for the 1st time.
// Test it desiredTestCount times, give it reasonable amount of time
// (twice as much as needed).
- QTRY_COMPARE_WITH_TIMEOUT(testIteration, desiredTestCount,
- testedInterval * desiredTestCount * 2);
+ const auto tryTimeout = tested_interval * desiredTestCount * 2;
+ QTRY_COMPARE_WITH_TIMEOUT(testIteration, desiredTestCount, tryTimeout);
}
-void tst_QTimer::remainingTimeInitial_data()
+void tst_QChronoTimer::remainingTimeInitial_data()
{
- QTest::addColumn<int>("startTimeMs");
+ using namespace std::chrono;
+
+ QTest::addColumn<nanoseconds>("startTimeNs");
QTest::addColumn<Qt::TimerType>("timerType");
- QTest::addRow("precise time 0ms") << 0 << Qt::PreciseTimer;
- QTest::addRow("precise time 1ms") << 1 << Qt::PreciseTimer;
- QTest::addRow("precise time 10ms") << 10 << Qt::PreciseTimer;
+ QTest::addRow("precisetiemr-0ns") << 0ns << Qt::PreciseTimer;
+ QTest::addRow("precisetimer-1ms") << nanoseconds{1ms} << Qt::PreciseTimer;
+ QTest::addRow("precisetimer-10ms") <<nanoseconds{10ms} << Qt::PreciseTimer;
- QTest::addRow("coarse time 0ms") << 0 << Qt::CoarseTimer;
- QTest::addRow("coarse time 1ms") << 1 << Qt::CoarseTimer;
- QTest::addRow("coarse time 10ms") << 10 << Qt::CoarseTimer;
+ QTest::addRow("coarsetimer-0ns") << 0ns << Qt::CoarseTimer;
+ QTest::addRow("coarsetimer-1ms") << nanoseconds{1ms} << Qt::CoarseTimer;
+ QTest::addRow("coarsetimer-10ms") << nanoseconds{10ms} << Qt::CoarseTimer;
}
-void tst_QTimer::remainingTimeInitial()
+void tst_QChronoTimer::remainingTimeInitial()
{
- QFETCH(int, startTimeMs);
+ QFETCH(std::chrono::nanoseconds, startTimeNs);
QFETCH(Qt::TimerType, timerType);
- QTimer timer;
+ QChronoTimer timer;
QCOMPARE(timer.timerType(), Qt::CoarseTimer);
timer.setTimerType(timerType);
QCOMPARE(timer.timerType(), timerType);
- timer.start(startTimeMs);
+ timer.setInterval(startTimeNs);
+ timer.start();
- const int rt = timer.remainingTime();
- QVERIFY2(rt >= 0 && rt <= startTimeMs, qPrintable(QString::number(rt)));
+ const std::chrono::nanoseconds rt = timer.remainingTime();
+ QCOMPARE_GE(rt, 0ns);
+ QCOMPARE_LE(rt, startTimeNs);
}
-void tst_QTimer::remainingTimeDuringActivation_data()
+void tst_QChronoTimer::remainingTimeDuringActivation_data()
{
QTest::addColumn<bool>("singleShot");
QTest::newRow("repeating") << false;
QTest::newRow("single-shot") << true;
}
-void tst_QTimer::remainingTimeDuringActivation()
+void tst_QChronoTimer::remainingTimeDuringActivation()
{
QFETCH(bool, singleShot);
- QTimer timer;
+ QChronoTimer timer;
timer.setSingleShot(singleShot);
- int remainingTime = 0; // not the expected value in either case
- connect(&timer, &QTimer::timeout,
- [&]() {
- remainingTime = timer.remainingTime();
- });
- QSignalSpy timeoutSpy(&timer, &QTimer::timeout);
- const int timeout = 20; // 20 ms is short enough and should not round down to 0 in any timer mode
- timer.start(timeout);
+ auto remainingTime = 0ns; // not the expected value in either case
+ connect(&timer, &QChronoTimer::timeout, this, [&]() { remainingTime = timer.remainingTime(); });
+ QSignalSpy timeoutSpy(&timer, &QChronoTimer::timeout);
+ // 20 ms is short enough and should not round down to 0 in any timer mode
+ constexpr auto timeout = 20ms;
+ timer.setInterval(timeout);
+ timer.start();
QVERIFY(timeoutSpy.wait());
if (singleShot)
- QCOMPARE(remainingTime, -1); // timer not running
- else
- QVERIFY2(remainingTime <= timeout && remainingTime > 0,
- qPrintable(QString::number(remainingTime)));
+ QCOMPARE_LT(remainingTime, 0ns); // timer not running
+ else {
+ QCOMPARE_LE(remainingTime, timeout);
+ QCOMPARE_GT(remainingTime, 0ns);
+ }
if (!singleShot) {
// do it again - see QTBUG-46940
- remainingTime = -1;
+ remainingTime = std::chrono::milliseconds::min();
QVERIFY(timeoutSpy.wait());
- QVERIFY2(remainingTime <= timeout && remainingTime > 0,
- qPrintable(QString::number(remainingTime)));
+ QCOMPARE_LE(remainingTime, timeout);
+ QCOMPARE_GT(remainingTime, 0ns);
}
}
namespace {
-
template <typename T>
- std::chrono::milliseconds to_ms(T t)
- { return std::chrono::duration_cast<std::chrono::milliseconds>(t); }
-
+ auto to_ms(T t)
+ {
+ using namespace std::chrono;
+ return duration_cast<milliseconds>(t);
+ }
} // unnamed namespace
-void tst_QTimer::basic_chrono()
+void tst_QChronoTimer::basic_chrono()
{
// duplicates zeroTimer, singleShotTimeout, interval and remainingTime
using namespace std::chrono;
- QTimer timer;
- QSignalSpy timeoutSpy(&timer, &QTimer::timeout);
- timer.setInterval(to_ms(nanoseconds(0)));
+ QChronoTimer timer;
+ QSignalSpy timeoutSpy(&timer, &QChronoTimer::timeout);
timer.start();
- QCOMPARE(timer.intervalAsDuration().count(), milliseconds::rep(0));
- QCOMPARE(timer.remainingTimeAsDuration().count(), milliseconds::rep(0));
+ QCOMPARE(timer.interval(), 0ns);
+ QCOMPARE(timer.remainingTime(), 0ns);
QCoreApplication::processEvents();
QCOMPARE(timeoutSpy.size(), 1);
timeoutSpy.clear();
- timer.start(milliseconds(100));
+ timer.setInterval(100ms);
+ timer.start();
QCOMPARE(timeoutSpy.size(), 0);
- QVERIFY(timeoutSpy.wait(TIMEOUT_TIMEOUT));
+ QVERIFY(timeoutSpy.wait(Timeout_Interval));
QVERIFY(timeoutSpy.size() > 0);
- int oldCount = timeoutSpy.size();
+ const qsizetype oldCount = timeoutSpy.size();
- QVERIFY(timeoutSpy.wait(TIMEOUT_TIMEOUT));
+ QVERIFY(timeoutSpy.wait(Timeout_Interval));
QVERIFY(timeoutSpy.size() > oldCount);
timeoutSpy.clear();
- timer.start(to_ms(microseconds(200000)));
- QCOMPARE(timer.intervalAsDuration().count(), milliseconds::rep(200));
- QTest::qWait(50);
+ timer.setInterval(200ms);
+ timer.start();
+ QCOMPARE(timer.interval(), 200ms);
+ QTest::qWait(50ms);
QCOMPARE(timeoutSpy.size(), 0);
- milliseconds rt = timer.remainingTimeAsDuration();
- QVERIFY2(rt.count() >= 50 && rt.count() <= 200, qPrintable(QString::number(rt.count())));
+ nanoseconds rt = timer.remainingTime();
+ QCOMPARE_GE(rt, 50ms);
+ QCOMPARE_LE(rt, 200ms);
timeoutSpy.clear();
timer.setSingleShot(true);
- timer.start(milliseconds(100));
- QVERIFY(timeoutSpy.wait(TIMEOUT_TIMEOUT));
+ timer.setInterval(100ms);
+ timer.start();
+ QVERIFY(timeoutSpy.wait(Timeout_Interval));
QCOMPARE(timeoutSpy.size(), 1);
- QTest::qWait(500);
+ QTest::qWait(500ms);
QCOMPARE(timeoutSpy.size(), 1);
}
-void tst_QTimer::livelock_data()
+void tst_QChronoTimer::livelock_data()
{
- QTest::addColumn<int>("interval");
- QTest::newRow("zero timer") << 0;
- QTest::newRow("non-zero timer") << 1;
- QTest::newRow("longer than sleep") << 20;
+ QTest::addColumn<std::chrono::nanoseconds>("interval");
+ QTest::newRow("zero-timer") << 0ns;
+ QTest::newRow("non-zero-timer") << std::chrono::nanoseconds{1ms};
+ QTest::newRow("longer-than-sleep") << std::chrono::nanoseconds{20ms};
}
/*!
@@ -471,20 +381,19 @@ void tst_QTimer::livelock_data()
*/
class LiveLockTester : public QObject
{
+ static constexpr QEvent::Type PostEventType = static_cast<QEvent::Type>(4002);
public:
- LiveLockTester(int i)
- : interval(i),
- timeoutsForFirst(0), timeoutsForExtra(0), timeoutsForSecond(0),
- postEventAtRightTime(false)
+ LiveLockTester(std::chrono::nanoseconds i)
+ : interval(i)
{
firstTimerId = startTimer(interval);
- extraTimerId = startTimer(interval + 80);
+ extraTimerId = startTimer(interval + 80ms);
secondTimerId = -1; // started later
}
bool event(QEvent *e) override
{
- if (e->type() == 4002) {
+ if (e->type() == PostEventType) {
// got the posted event
if (timeoutsForFirst == 1 && timeoutsForSecond == 0)
postEventAtRightTime = true;
@@ -499,7 +408,7 @@ public:
if (++timeoutsForFirst == 1) {
killTimer(extraTimerId);
extraTimerId = -1;
- QCoreApplication::postEvent(this, new QEvent(static_cast<QEvent::Type>(4002)));
+ QCoreApplication::postEvent(this, new QEvent(PostEventType));
secondTimerId = startTimer(interval);
}
} else if (te->timerId() == secondTimerId) {
@@ -513,17 +422,17 @@ public:
killTimer(te->timerId());
}
- const int interval;
- int firstTimerId;
- int secondTimerId;
- int extraTimerId;
- int timeoutsForFirst;
- int timeoutsForExtra;
- int timeoutsForSecond;
- bool postEventAtRightTime;
+ const std::chrono::nanoseconds interval;
+ int firstTimerId = -1;
+ int secondTimerId = -1;
+ int extraTimerId = -1;
+ int timeoutsForFirst = 0;
+ int timeoutsForExtra = 0;
+ int timeoutsForSecond = 0;
+ bool postEventAtRightTime = false;
};
-void tst_QTimer::livelock()
+void tst_QChronoTimer::livelock()
{
/*
New timers created in timer event handlers should not be sent
@@ -532,9 +441,9 @@ void tst_QTimer::livelock()
events (since new posted events are not sent until the next
iteration of the eventloop either).
*/
- QFETCH(int, interval);
+ QFETCH(std::chrono::nanoseconds, interval);
LiveLockTester tester(interval);
- QTest::qWait(180); // we have to use wait here, since we're testing timers with a non-zero timeout
+ QTest::qWait(180ms); // we have to use wait here, since we're testing timers with a non-zero timeout
QTRY_COMPARE(tester.timeoutsForFirst, 1);
QCOMPARE(tester.timeoutsForExtra, 0);
QTRY_COMPARE(tester.timeoutsForSecond, 1);
@@ -544,12 +453,12 @@ void tst_QTimer::livelock()
class TimerInfiniteRecursionObject : public QObject
{
public:
- bool inTimerEvent;
- bool timerEventRecursed;
- int interval;
+ bool inTimerEvent = false;
+ bool timerEventRecursed = false;
+ std::chrono::nanoseconds interval;
- TimerInfiniteRecursionObject(int interval)
- : inTimerEvent(false), timerEventRecursed(false), interval(interval)
+ TimerInfiniteRecursionObject(std::chrono::nanoseconds interval)
+ : interval(interval)
{ }
void timerEvent(QTimerEvent *timerEvent) override
@@ -563,7 +472,8 @@ public:
inTimerEvent = true;
QEventLoop eventLoop;
- QTimer::singleShot(qMax(100, interval * 2), &eventLoop, SLOT(quit()));
+ QChronoTimer::singleShot(std::max<std::chrono::nanoseconds>(100ms, interval * 2),
+ &eventLoop, &QEventLoop::quit);
eventLoop.exec();
inTimerEvent = false;
@@ -572,26 +482,27 @@ public:
}
};
-void tst_QTimer::timerInfiniteRecursion_data()
+void tst_QChronoTimer::timerInfiniteRecursion_data()
{
- QTest::addColumn<int>("interval");
- QTest::newRow("zero timer") << 0;
- QTest::newRow("non-zero timer") << 1;
- QTest::newRow("10ms timer") << 10;
- QTest::newRow("11ms timer") << 11;
- QTest::newRow("100ms timer") << 100;
- QTest::newRow("1s timer") << 1000;
+ QTest::addColumn<std::chrono::nanoseconds>("interval");
+ QTest::newRow("zero timer") << 0ns;
+ QTest::newRow("non-zero timer") << std::chrono::nanoseconds{1ms};
+ QTest::newRow("10ms timer") << std::chrono::nanoseconds{10ms};
+ QTest::newRow("11ms timer") << std::chrono::nanoseconds{11ms};
+ QTest::newRow("100ms timer") << std::chrono::nanoseconds{100ms};
+ QTest::newRow("1s timer") << std::chrono::nanoseconds{1000ms};
}
-void tst_QTimer::timerInfiniteRecursion()
+void tst_QChronoTimer::timerInfiniteRecursion()
{
- QFETCH(int, interval);
+ QFETCH(std::chrono::nanoseconds, interval);
TimerInfiniteRecursionObject object(interval);
(void) object.startTimer(interval);
QEventLoop eventLoop;
- QTimer::singleShot(qMax(100, interval * 2), &eventLoop, SLOT(quit()));
+ QChronoTimer::singleShot(std::max<std::chrono::nanoseconds>(100ms, interval * 2), &eventLoop,
+ &QEventLoop::quit);
eventLoop.exec();
QVERIFY(!object.timerEventRecursed);
@@ -613,10 +524,10 @@ public:
{
if (++times == target) {
killTimer(timerEvent->timerId());
- emit done();
+ Q_EMIT done();
} if (recurse) {
QEventLoop eventLoop;
- QTimer::singleShot(100, &eventLoop, SLOT(quit()));
+ QChronoTimer::singleShot(100ms, &eventLoop, &QEventLoop::quit);
eventLoop.exec();
}
}
@@ -625,21 +536,21 @@ signals:
void done();
};
-void tst_QTimer::recurringTimer_data()
+void tst_QChronoTimer::recurringTimer_data()
{
- QTest::addColumn<int>("interval");
+ QTest::addColumn<std::chrono::nanoseconds>("interval");
QTest::addColumn<bool>("recurse");
// make sure that eventloop recursion doesn't affect timer recurrence
- QTest::newRow("zero timer, don't recurse") << 0 << false;
- QTest::newRow("zero timer, recurse") << 0 << true;
- QTest::newRow("non-zero timer, don't recurse") << 1 << false;
- QTest::newRow("non-zero timer, recurse") << 1 << true;
+ QTest::newRow("zero timer, don't recurse") << 0ns << false;
+ QTest::newRow("zero timer, recurse") << 0ns << true;
+ QTest::newRow("non-zero timer, don't recurse") << std::chrono::nanoseconds{1ms} << false;
+ QTest::newRow("non-zero timer, recurse") << std::chrono::nanoseconds{1ms} << true;
}
-void tst_QTimer::recurringTimer()
+void tst_QChronoTimer::recurringTimer()
{
const int target = 5;
- QFETCH(int, interval);
+ QFETCH(std::chrono::nanoseconds, interval);
QFETCH(bool, recurse);
RecurringTimerObject object(target);
@@ -652,54 +563,56 @@ void tst_QTimer::recurringTimer()
QCOMPARE(object.times, target);
}
-void tst_QTimer::deleteLaterOnQTimer()
+void tst_QChronoTimer::deleteLaterOnQChronoTimer()
{
- QTimer *timer = new QTimer;
- connect(timer, SIGNAL(timeout()), timer, SLOT(deleteLater()));
+ QChronoTimer *timer = new QChronoTimer;
+ connect(timer, &QChronoTimer::timeout, timer, &QObject::deleteLater);
QSignalSpy destroyedSpy(timer, &QObject::destroyed);
- timer->setInterval(1);
+ timer->setInterval(1ms);
timer->setSingleShot(true);
timer->start();
- QPointer<QTimer> pointer = timer;
+ QPointer<QChronoTimer> pointer = timer;
QVERIFY(destroyedSpy.wait());
QVERIFY(pointer.isNull());
}
-#define MOVETOTHREAD_TIMEOUT 200
-#define MOVETOTHREAD_WAIT 300
+static constexpr auto MoveToThread_Timeout = 200ms;
+static constexpr auto MoveToThread_Wait = 300ms;
-void tst_QTimer::moveToThread()
+void tst_QChronoTimer::moveToThread()
{
#if defined(Q_OS_WIN32)
QSKIP("Does not work reliably on Windows :(");
#elif defined(Q_OS_MACOS)
QSKIP("Does not work reliably on macOS 10.12+ (QTBUG-59679)");
#endif
- QTimer ti1;
- QTimer ti2;
- ti1.setSingleShot(true);
- ti1.start(MOVETOTHREAD_TIMEOUT);
- ti2.start(MOVETOTHREAD_TIMEOUT);
- QVERIFY((ti1.timerId() & 0xffffff) != (ti2.timerId() & 0xffffff));
+ QChronoTimer timer1{MoveToThread_Timeout};
+ QChronoTimer timer2{MoveToThread_Timeout};
+ timer1.setSingleShot(true);
+ timer1.start();
+ timer2.start();
+ QVERIFY((timer1.id() & 0xffffff) != (timer2.id() & 0xffffff));
QThread tr;
- ti1.moveToThread(&tr);
- connect(&ti1,SIGNAL(timeout()), &tr, SLOT(quit()));
+ timer1.moveToThread(&tr);
+ connect(&timer1, &QChronoTimer::timeout, &tr, &QThread::quit);
tr.start();
- QTimer ti3;
- ti3.start(MOVETOTHREAD_TIMEOUT);
- QVERIFY((ti3.timerId() & 0xffffff) != (ti2.timerId() & 0xffffff));
- QVERIFY((ti3.timerId() & 0xffffff) != (ti1.timerId() & 0xffffff));
- QTest::qWait(MOVETOTHREAD_WAIT);
+ QChronoTimer ti3{MoveToThread_Timeout};
+ ti3.start();
+ QVERIFY((ti3.id() & 0xffffff) != (timer2.id() & 0xffffff));
+ QVERIFY((ti3.id() & 0xffffff) != (timer1.id() & 0xffffff));
+ QTest::qWait(MoveToThread_Wait);
QVERIFY(tr.wait());
- ti2.stop();
- QTimer ti4;
- ti4.start(MOVETOTHREAD_TIMEOUT);
+ timer2.stop();
+ QChronoTimer ti4{MoveToThread_Timeout};
+ ti4.start();
ti3.stop();
- ti2.start(MOVETOTHREAD_TIMEOUT);
- ti3.start(MOVETOTHREAD_TIMEOUT);
- QVERIFY((ti4.timerId() & 0xffffff) != (ti2.timerId() & 0xffffff));
- QVERIFY((ti3.timerId() & 0xffffff) != (ti2.timerId() & 0xffffff));
- QVERIFY((ti3.timerId() & 0xffffff) != (ti1.timerId() & 0xffffff));
+ timer2.setInterval(MoveToThread_Timeout);
+ timer2.start();
+ ti3.setInterval(MoveToThread_Timeout);
+ ti3.start();
+ QVERIFY((ti4.id() & 0xffffff) != (timer2.id() & 0xffffff));
+ QVERIFY((ti3.id() & 0xffffff) != (timer2.id() & 0xffffff));
+ QVERIFY((ti3.id() & 0xffffff) != (timer1.id() & 0xffffff));
}
class RestartedTimerFiresTooSoonObject : public QObject
@@ -709,24 +622,22 @@ class RestartedTimerFiresTooSoonObject : public QObject
public:
QBasicTimer m_timer;
- int m_interval;
+ std::chrono::milliseconds m_interval = 0ms;
QElapsedTimer m_elapsedTimer;
QEventLoop eventLoop;
- inline RestartedTimerFiresTooSoonObject()
- : QObject(), m_interval(0)
- { }
+ RestartedTimerFiresTooSoonObject() = default;
void timerFired()
{
- static int interval = 1000;
+ static std::chrono::milliseconds interval = 1s;
m_interval = interval;
m_elapsedTimer.start();
m_timer.start(interval, this);
// alternate between single-shot and 1 sec
- interval = interval ? 0 : 1000;
+ interval = interval > 0ms ? 0ms : 1s;
}
void timerEvent(QTimerEvent* ev) override
@@ -736,7 +647,7 @@ public:
m_timer.stop();
- int elapsed = m_elapsedTimer.elapsed();
+ std::chrono::nanoseconds elapsed = m_elapsedTimer.durationElapsed();
if (elapsed < m_interval / 2) {
// severely too early!
@@ -757,7 +668,7 @@ public:
}
};
-void tst_QTimer::restartedTimerFiresTooSoon()
+void tst_QChronoTimer::restartedTimerFiresTooSoon()
{
RestartedTimerFiresTooSoonObject object;
object.timerFired();
@@ -769,16 +680,16 @@ class LongLastingSlotClass : public QObject
Q_OBJECT
public:
- LongLastingSlotClass(QTimer *timer) : count(0), timer(timer) {}
+ LongLastingSlotClass(QChronoTimer *timer) : timer(timer) { }
public slots:
void longLastingSlot()
{
- // Don't use QTimer for this, because we are testing it.
+ // Don't use QChronoTimer for this, because we are testing it.
QElapsedTimer control;
control.start();
- while (control.elapsed() < 200) {
- for (int c = 0; c < 100000; c++) {} // Mindless looping.
+ while (control.durationElapsed() < 200ms) {
+ for (int c = 0; c < 100'000; c++) {} // Mindless looping.
}
if (++count >= 2) {
timer->stop();
@@ -786,29 +697,28 @@ public slots:
}
public:
- int count;
- QTimer *timer;
+ int count = 0;
+ QChronoTimer *timer;
};
-void tst_QTimer::timerFiresOnlyOncePerProcessEvents_data()
+void tst_QChronoTimer::timerFiresOnlyOncePerProcessEvents_data()
{
- QTest::addColumn<int>("interval");
- QTest::newRow("zero timer") << 0;
- QTest::newRow("non-zero timer") << 10;
+ QTest::addColumn<std::chrono::nanoseconds>("interval");
+ QTest::newRow("zero-timer") << 0ns;
+ QTest::newRow("non-zero-timer") << std::chrono::nanoseconds{10ms};
}
-void tst_QTimer::timerFiresOnlyOncePerProcessEvents()
+void tst_QChronoTimer::timerFiresOnlyOncePerProcessEvents()
{
- QFETCH(int, interval);
+ QFETCH(std::chrono::nanoseconds, interval);
- QTimer t;
+ QChronoTimer t{interval};
LongLastingSlotClass longSlot(&t);
- t.start(interval);
- connect(&t, SIGNAL(timeout()), &longSlot, SLOT(longLastingSlot()));
+ t.start();
+ connect(&t, &QChronoTimer::timeout, &longSlot, &LongLastingSlotClass::longLastingSlot);
// Loop because there may be other events pending.
- while (longSlot.count == 0) {
+ while (longSlot.count == 0)
QCoreApplication::processEvents(QEventLoop::WaitForMoreEvents);
- }
QCOMPARE(longSlot.count, 1);
}
@@ -816,49 +726,44 @@ void tst_QTimer::timerFiresOnlyOncePerProcessEvents()
class TimerIdPersistsAfterThreadExitThread : public QThread
{
public:
- QTimer *timer;
- int timerId, returnValue;
-
- TimerIdPersistsAfterThreadExitThread()
- : QThread(), timer(0), timerId(-1), returnValue(-1)
- { }
- ~TimerIdPersistsAfterThreadExitThread()
- {
- delete timer;
- }
+ std::unique_ptr<QChronoTimer> timer;
+ int timerId = -1;
+ int returnValue = -1;
void run() override
{
QEventLoop eventLoop;
- timer = new QTimer;
- connect(timer, SIGNAL(timeout()), &eventLoop, SLOT(quit()));
- timer->start(100);
- timerId = timer->timerId();
+ timer = std::make_unique<QChronoTimer>();
+ connect(timer.get(), &QChronoTimer::timeout, &eventLoop, &QEventLoop::quit);
+ timer->setInterval(100ms);
+ timer->start();
+ timerId = timer->id();
returnValue = eventLoop.exec();
}
};
-void tst_QTimer::timerIdPersistsAfterThreadExit()
+void tst_QChronoTimer::timerIdPersistsAfterThreadExit()
{
TimerIdPersistsAfterThreadExitThread thread;
thread.start();
- QVERIFY(thread.wait(30000));
+ QVERIFY(thread.wait(30s));
QCOMPARE(thread.returnValue, 0);
// even though the thread has exited, and the event dispatcher destroyed, the timer is still
// "active", meaning the timer id should NOT be reused (i.e. the event dispatcher should not
// have unregistered it)
- int timerId = thread.startTimer(100);
+ int timerId = thread.startTimer(100ms);
QVERIFY((timerId & 0xffffff) != (thread.timerId & 0xffffff));
}
-void tst_QTimer::cancelLongTimer()
+void tst_QChronoTimer::cancelLongTimer()
{
- QTimer timer;
+ QChronoTimer timer{1h};
timer.setSingleShot(true);
- timer.start(1000 * 60 * 60); //set timer for 1 hour
+ timer.start();
QCoreApplication::processEvents();
- QVERIFY(timer.isActive()); //if the timer completes immediately with an error, then this will fail
+ // If the timer completes immediately with an error, then this will fail
+ QVERIFY(timer.isActive());
timer.stop();
QVERIFY(!timer.isActive());
}
@@ -872,34 +777,13 @@ public:
int count = 0;
};
-void tst_QTimer::singleShotStaticFunctionZeroTimeout()
-{
- {
- TimeoutCounter counter;
-
- QTimer::singleShot(0, &counter, SLOT(timeout()));
- QTRY_COMPARE(counter.count, 1);
- QTest::qWait(500);
- QCOMPARE(counter.count, 1);
- }
-
- {
- TimeoutCounter counter;
-
- QTimer::singleShot(0, &counter, &TimeoutCounter::timeout);
- QTRY_COMPARE(counter.count, 1);
- QTest::qWait(500);
- QCOMPARE(counter.count, 1);
- }
-}
-
class RecursOnTimeoutAndStopTimerTimer : public QObject
{
Q_OBJECT
public:
- QTimer *one;
- QTimer *two;
+ QChronoTimer *one;
+ QChronoTimer *two;
public slots:
void onetrigger()
@@ -913,14 +797,14 @@ public slots:
}
};
-void tst_QTimer::recurseOnTimeoutAndStopTimer()
+void tst_QChronoTimer::recurseOnTimeoutAndStopTimer()
{
QEventLoop eventLoop;
- QTimer::singleShot(1000, &eventLoop, SLOT(quit()));
+ QChronoTimer::singleShot(1s, &eventLoop, &QEventLoop::quit);
RecursOnTimeoutAndStopTimerTimer t;
- t.one = new QTimer(&t);
- t.two = new QTimer(&t);
+ t.one = new QChronoTimer(&t);
+ t.two = new QChronoTimer(&t);
QObject::connect(t.one, SIGNAL(timeout()), &t, SLOT(onetrigger()));
QObject::connect(t.two, SIGNAL(timeout()), &t, SLOT(twotrigger()));
@@ -966,128 +850,6 @@ public:
}
};
-void tst_QTimer::singleShotToFunctors()
-{
- int count = 0;
- _e.reset(new QEventLoop);
- QEventLoop e;
-
- QTimer::singleShot(0, CountedStruct(&count));
- QCoreApplication::processEvents();
- QCOMPARE(count, 1);
-
- QTimer::singleShot(0, &StaticEventLoop::quitEventLoop);
- QCOMPARE(_e->exec(), 0);
-
- QTimer::singleShot(0, &StaticEventLoop::quitEventLoop_noexcept);
- QCOMPARE(_e->exec(), 0);
-
- QThread t1;
- QObject c1;
- c1.moveToThread(&t1);
-
- QObject::connect(&t1, SIGNAL(started()), &e, SLOT(quit()));
- t1.start();
- QCOMPARE(e.exec(), 0);
-
- QTimer::singleShot(0, &c1, CountedStruct(&count, &t1));
- QTRY_COMPARE(count, 2);
-
- t1.quit();
- t1.wait();
-
- _t = new QThread;
- QObject c2;
- c2.moveToThread(_t);
-
- QObject::connect(_t, SIGNAL(started()), &e, SLOT(quit()));
- _t->start();
- QCOMPARE(e.exec(), 0);
-
- QTimer::singleShot(0, &c2, &StaticEventLoop::quitEventLoop);
- QCOMPARE(_e->exec(), 0);
-
- _t->quit();
- _t->wait();
- _t->deleteLater();
- _t = nullptr;
-
- {
- QObject c3;
- QTimer::singleShot(500, &c3, CountedStruct(&count));
- }
- QTest::qWait(800); // Wait until the singleshot timer would have timed out
- QCOMPARE(count, 2);
-
- QTimer::singleShot(0, [&count] { ++count; });
- QTRY_COMPARE(count, 3);
-
- QObject context;
- QThread thread;
-
- context.moveToThread(&thread);
- QObject::connect(&thread, SIGNAL(started()), &e, SLOT(quit()));
- thread.start();
- QCOMPARE(e.exec(), 0);
-
- QTimer::singleShot(0, &context, [&count, &thread] { ++count; QCOMPARE(QThread::currentThread(), &thread); });
- QTRY_COMPARE(count, 4);
-
- thread.quit();
- thread.wait();
-
- struct MoveOnly : CountedStruct {
- Q_DISABLE_COPY(MoveOnly)
- MoveOnly(MoveOnly &&o) : CountedStruct(std::move(o)) {};
- MoveOnly(int *c) : CountedStruct(c) {}
- };
- QTimer::singleShot(0, MoveOnly(&count));
- QTRY_COMPARE(count, 5);
-
- _e.reset();
- _t = nullptr;
-}
-
-void tst_QTimer::singleShot_chrono()
-{
- // duplicates singleShotStaticFunctionZeroTimeout and singleShotToFunctors
- using namespace std::chrono;
- {
- TimeoutCounter counter;
-
- QTimer::singleShot(hours(0), &counter, SLOT(timeout()));
- QTRY_COMPARE(counter.count, 1);
- QTest::qWait(500);
- QCOMPARE(counter.count, 1);
- }
-
- {
- TimeoutCounter counter;
-
- QTimer::singleShot(hours(0), &counter, &TimeoutCounter::timeout);
- QTRY_COMPARE(counter.count, 1);
- QTest::qWait(500);
- QCOMPARE(counter.count, 1);
- }
-
- int count = 0;
- QTimer::singleShot(to_ms(microseconds(0)), CountedStruct(&count));
- QTRY_COMPARE(count, 1);
-
- _e.reset(new QEventLoop);
- QTimer::singleShot(0, &StaticEventLoop::quitEventLoop);
- QCOMPARE(_e->exec(), 0);
-
- QObject c3;
- QTimer::singleShot(milliseconds(500), &c3, CountedStruct(&count));
- QTRY_COMPARE(count, 2);
-
- QTimer::singleShot(0, [&count] { ++count; });
- QTRY_COMPARE(count, 3);
-
- _e.reset();
-}
-
class DontBlockEvents : public QObject
{
Q_OBJECT
@@ -1109,22 +871,21 @@ DontBlockEvents::DontBlockEvents()
count = 0;
total = 0;
+ const std::chrono::milliseconds intervals[] = {2s, 2500ms, 3s, 5s, 1s, 2s};
// need a few unrelated timers running to reproduce the bug.
- (new QTimer(this))->start(2000);
- (new QTimer(this))->start(2500);
- (new QTimer(this))->start(3000);
- (new QTimer(this))->start(5000);
- (new QTimer(this))->start(1000);
- (new QTimer(this))->start(2000);
-
- m_timer.start(1, this);
+ for (auto dur : intervals) {
+ auto *t = new QChronoTimer(dur, this);
+ t->start();
+ }
+
+ m_timer.start(1ms, this);
}
void DontBlockEvents::timerEvent(QTimerEvent* event)
{
if (event->timerId() == m_timer.timerId()) {
- QMetaObject::invokeMethod(this, "paintEvent", Qt::QueuedConnection);
- m_timer.start(0, this);
+ QMetaObject::invokeMethod(this, &DontBlockEvents::paintEvent, Qt::QueuedConnection);
+ m_timer.start(0ms, this);
count++;
QCOMPARE(count, 1);
total++;
@@ -1139,10 +900,10 @@ void DontBlockEvents::paintEvent()
// This is a regression test for QTBUG-13633, where a timer with a zero
// timeout that was restarted by the event handler could starve other timers.
-void tst_QTimer::dontBlockEvents()
+void tst_QChronoTimer::dontBlockEvents()
{
DontBlockEvents t;
- QTest::qWait(60);
+ QTest::qWait(60ms);
QTRY_VERIFY(t.total > 2);
}
@@ -1154,16 +915,16 @@ public:
public slots:
void repeatThisSlot()
{
- QMetaObject::invokeMethod(this, "repeatThisSlot", Qt::QueuedConnection);
+ QMetaObject::invokeMethod(this, &SlotRepeater::repeatThisSlot, Qt::QueuedConnection);
}
};
-void tst_QTimer::postedEventsShouldNotStarveTimers()
+void tst_QChronoTimer::postedEventsShouldNotStarveTimers()
{
- QTimer timer;
- timer.setInterval(0);
+ QChronoTimer timer;
+ timer.setInterval(0ns);
timer.setSingleShot(false);
- QSignalSpy timeoutSpy(&timer, &QTimer::timeout);
+ QSignalSpy timeoutSpy(&timer, &QChronoTimer::timeout);
timer.start();
SlotRepeater slotRepeater;
slotRepeater.repeatThisSlot();
@@ -1179,75 +940,33 @@ struct DummyFunctor {
};
QThread *DummyFunctor::callThread = nullptr;
-void tst_QTimer::crossThreadSingleShotToFunctor_data()
-{
- QTest::addColumn<int>("timeout");
-
- QTest::addRow("zero-timer") << 0;
- QTest::addRow("1ms") << 1;
-}
-
-void tst_QTimer::crossThreadSingleShotToFunctor()
-{
- QFETCH(int, timeout);
- // We're also testing for crashes here, so the test simply running to
- // completion is part of the success
- DummyFunctor::callThread = nullptr;
-
- QThread t;
- std::unique_ptr<QObject> o(new QObject());
- o->moveToThread(&t);
-
- QTimer::singleShot(timeout, o.get(), DummyFunctor());
-
- // wait enough time for the timer to have timed out before the timer
- // could be start in the receiver's thread.
- QTest::qWait(10 + timeout * 10);
- t.start();
- t.wait();
- QCOMPARE(DummyFunctor::callThread, &t);
-
- // continue with a stress test - the calling thread is busy, the
- // timer should still fire and no crashes.
- DummyFunctor::callThread = nullptr;
- t.start();
- for (int i = 0; i < 10000; i++)
- QTimer::singleShot(timeout, o.get(), DummyFunctor());
-
- t.wait();
- o.reset();
-
- QCOMPARE(DummyFunctor::callThread, &t);
-}
-
-void tst_QTimer::callOnTimeout()
+void tst_QChronoTimer::callOnTimeout()
{
- QTimer timer;
- QSignalSpy timeoutSpy(&timer, &QTimer::timeout);
- timer.setInterval(0);
+ QChronoTimer timer;
+ QSignalSpy timeoutSpy(&timer, &QChronoTimer::timeout);
timer.start();
- auto context = new QObject();
+ auto context = std::make_unique<QObject>();
int count = 0;
timer.callOnTimeout([&count] { count++; });
- QMetaObject::Connection connection = timer.callOnTimeout(context, [&count] { count++; });
- timer.callOnTimeout(&timer, &QTimer::stop);
+ QMetaObject::Connection connection = timer.callOnTimeout(context.get(), [&count] { count++; });
+ timer.callOnTimeout(&timer, &QChronoTimer::stop);
- QTest::qWait(100);
+ QTest::qWait(100ms);
QCOMPARE(count, 2);
QCOMPARE(timeoutSpy.size(), 1);
// Test that connection is bound to context lifetime
QVERIFY(connection);
- delete context;
+ context.reset();
QVERIFY(!connection);
}
-void tst_QTimer::bindToTimer()
+void tst_QChronoTimer::bindToTimer()
{
- QTimer timer;
+ QChronoTimer timer;
// singleShot property
QProperty<bool> singleShot;
@@ -1260,14 +979,14 @@ void tst_QTimer::bindToTimer()
QVERIFY(!singleShot);
// interval property
- QProperty<int> interval;
+ QProperty<std::chrono::nanoseconds> interval;
interval.setBinding([&](){ return timer.interval(); });
- QCOMPARE(timer.interval(), interval);
+ QCOMPARE(timer.interval(), interval.value());
- timer.setInterval(10);
- QCOMPARE(interval, 10);
- timer.setInterval(100);
- QCOMPARE(interval, 100);
+ timer.setInterval(10ms);
+ QCOMPARE(interval.value(), 10ms);
+ timer.setInterval(100ms);
+ QCOMPARE(interval.value(), 100ms);
// timerType property
QProperty<Qt::TimerType> timerType;
@@ -1285,33 +1004,43 @@ void tst_QTimer::bindToTimer()
active.setBinding([&](){ return timer.isActive(); });
QCOMPARE(active, timer.isActive());
- timer.start(1000);
+ timer.setInterval(1s);
+ timer.start();
QVERIFY(active);
timer.stop();
QVERIFY(!active);
+ // Also test that using negative interval updates the binding correctly
+ timer.setInterval(100ms);
+ timer.start();
+ QVERIFY(active);
+
auto ignoreMsg = [] {
QTest::ignoreMessage(QtWarningMsg,
"QObject::startTimer: Timers cannot have negative intervals");
};
- // also test that using negative interval updates the binding correctly
- timer.start(100);
- QVERIFY(active);
ignoreMsg();
- timer.setInterval(-100);
+ timer.setInterval(-100ms);
+ ignoreMsg();
+ timer.start();
QVERIFY(!active);
- timer.start(100);
+
+ timer.setInterval(100ms);
+ timer.start();
QVERIFY(active);
+
+ ignoreMsg();
+ timer.setInterval(-100ms);
ignoreMsg();
- timer.start(-100);
+ timer.start();
QVERIFY(!active);
}
-void tst_QTimer::bindTimer()
+void tst_QChronoTimer::bindTimer()
{
- QTimer timer;
+ QChronoTimer timer;
// singleShot property
QVERIFY(!timer.isSingleShot());
@@ -1325,19 +1054,19 @@ void tst_QTimer::bindTimer()
QVERIFY(!timer.isSingleShot());
// interval property
- QCOMPARE(timer.interval(), 0);
+ QCOMPARE(timer.interval(), 0ns);
- QProperty<int> interval;
+ QProperty<std::chrono::nanoseconds> interval;
timer.bindableInterval().setBinding(Qt::makePropertyBinding(interval));
- interval = 10;
- QCOMPARE(timer.interval(), 10);
- interval = 100;
- QCOMPARE(timer.interval(), 100);
- timer.setInterval(50);
- QCOMPARE(timer.interval(), 50);
- interval = 30;
- QCOMPARE(timer.interval(), 50);
+ interval = 10ms;
+ QCOMPARE(timer.interval(), 10ms);
+ interval = 100ms;
+ QCOMPARE(timer.interval(), 100ms);
+ timer.setInterval(50ms);
+ QCOMPARE(timer.interval(), 50ms);
+ interval = 30ms;
+ QCOMPARE(timer.interval(), 50ms);
// timerType property
QCOMPARE(timer.timerType(), Qt::CoarseTimer);
@@ -1351,21 +1080,22 @@ void tst_QTimer::bindTimer()
QCOMPARE(timer.timerType(), Qt::VeryCoarseTimer);
}
-void tst_QTimer::automatedBindingTests()
+void tst_QChronoTimer::automatedBindingTests()
{
- QTimer timer;
+ QChronoTimer timer;
QVERIFY(!timer.isSingleShot());
QTestPrivate::testReadWritePropertyBasics(timer, true, false, "singleShot");
if (QTest::currentTestFailed()) {
- qDebug("Failed property test for QTimer::singleShot");
+ qDebug("Failed property test for QChronoTimer::singleShot");
return;
}
- QCOMPARE_NE(timer.interval(), 10);
- QTestPrivate::testReadWritePropertyBasics(timer, 10, 20, "interval");
+ QCOMPARE_NE(timer.interval(), 10ms);
+ using NSec = std::chrono::nanoseconds;
+ QTestPrivate::testReadWritePropertyBasics(timer, NSec{10ms}, NSec{20ms}, "interval");
if (QTest::currentTestFailed()) {
- qDebug("Failed property test for QTimer::interval");
+ qDebug("Failed property test for QChronoTimer::interval");
return;
}
@@ -1373,48 +1103,50 @@ void tst_QTimer::automatedBindingTests()
QTestPrivate::testReadWritePropertyBasics(timer, Qt::PreciseTimer, Qt::CoarseTimer,
"timerType");
if (QTest::currentTestFailed()) {
- qDebug("Failed property test for QTimer::timerType");
+ qDebug("Failed property test for QChronoTimer::timerType");
return;
}
- timer.start(1000);
+ timer.setInterval(1s);
+ timer.start();
QVERIFY(timer.isActive());
QTestPrivate::testReadOnlyPropertyBasics(timer, true, false, "active",
[&timer]() { timer.stop(); });
if (QTest::currentTestFailed()) {
- qDebug("Failed property test for QTimer::active");
+ qDebug("Failed property test for QChronoTimer::active");
return;
}
}
-void tst_QTimer::negativeInterval()
+void tst_QChronoTimer::negativeInterval()
{
+ QChronoTimer timer;
+
auto ignoreMsg = [] {
QTest::ignoreMessage(QtWarningMsg,
"QObject::startTimer: Timers cannot have negative intervals");
};
- QTimer timer;
-
- // Starting with a negative interval does not change active state.
ignoreMsg();
- timer.start(-100ms);
+ // Setting a negative interval does not change the active state.
+ timer.setInterval(-100ms);
+ ignoreMsg();
+ timer.start();
QVERIFY(!timer.isActive());
- // Updating the interval to a negative value stops the timer and changes
- // the active state.
- timer.start(100ms);
+ // Starting a timer that has a positive interval, the active state is changed
+ timer.setInterval(100ms);
+ timer.start();
QVERIFY(timer.isActive());
+
ignoreMsg();
- timer.setInterval(-100);
+ // Setting a negative interval on an already running timer...
+ timer.setInterval(-100ms);
+ // ... the timer is stopped and the active state is changed
QVERIFY(!timer.isActive());
- // Starting with a negative interval when already started leads to stop
- // and inactive state.
- timer.start(100);
- QVERIFY(timer.isActive());
- ignoreMsg();
- timer.start(-100ms);
+ // Calling start on a timer that has a negative interval, does not change the active state
+ timer.start();
QVERIFY(!timer.isActive());
}
@@ -1437,16 +1169,16 @@ public:
switch (callType)
{
case String:
- QTimer::singleShot(0, this, SLOT(stringSlot()));
+ QChronoTimer::singleShot(0ns, this, SLOT(stringSlot()));
break;
case PMF:
- QTimer::singleShot(0, this, &OrderHelper::pmfSlot);
+ QChronoTimer::singleShot(0ns, this, &OrderHelper::pmfSlot);
break;
case Functor:
- QTimer::singleShot(0, this, [this]() { functorSlot(); });
+ QChronoTimer::singleShot(0ns, this, [this]() { functorSlot(); });
break;
case FunctorNoCtx:
- QTimer::singleShot(0, [this]() { functorNoCtxSlot(); });
+ QChronoTimer::singleShot(0ns, [this]() { functorNoCtxSlot(); });
break;
}
}
@@ -1460,7 +1192,7 @@ public slots:
Q_DECLARE_METATYPE(OrderHelper::CallType)
-void tst_QTimer::timerOrder()
+void tst_QChronoTimer::timerOrder()
{
QFETCH(QList<OrderHelper::CallType>, calls);
@@ -1472,7 +1204,7 @@ void tst_QTimer::timerOrder()
QTRY_COMPARE(helper.calls, calls);
}
-void tst_QTimer::timerOrder_data()
+void tst_QChronoTimer::timerOrder_data()
{
QTest::addColumn<QList<OrderHelper::CallType>>("calls");
@@ -1489,7 +1221,7 @@ void tst_QTimer::timerOrder_data()
} while (std::next_permutation(calls.begin(), calls.end()));
}
-void tst_QTimer::timerOrderBackgroundThread()
+void tst_QChronoTimer::timerOrderBackgroundThread()
{
auto *thread = QThread::create([this]() { timerOrder(); });
thread->start();
@@ -1497,43 +1229,34 @@ void tst_QTimer::timerOrderBackgroundThread()
delete thread;
}
-struct StaticSingleShotUser
+void tst_QChronoTimer::timerPrecision()
{
- StaticSingleShotUser()
- {
- for (auto call : calls())
- helper.triggerCall(call);
- }
- OrderHelper helper;
-
- static QList<OrderHelper::CallType> calls()
- {
- return {OrderHelper::String, OrderHelper::PMF,
- OrderHelper::Functor, OrderHelper::FunctorNoCtx};
- }
-};
-
-// NOTE: to prevent any static initialization order fiasco, we implement
-// initMain() to instantiate staticSingleShotUser before qApp
-
-static StaticSingleShotUser *s_staticSingleShotUser = nullptr;
+ using namespace std::chrono;
+ steady_clock::time_point t1{};
+ steady_clock::time_point t2{};
-void tst_QTimer::initMain()
-{
- s_staticSingleShotUser = new StaticSingleShotUser;
-}
+ QEventLoop loop;
-void tst_QTimer::cleanupTestCase()
-{
- delete s_staticSingleShotUser;
-}
+ QChronoTimer zeroTimer{0ns};
+ zeroTimer.setTimerType(Qt::PreciseTimer);
+ zeroTimer.setSingleShot(true);
+ connect(&zeroTimer, &QChronoTimer::timeout, this, [&t1] { t1 = steady_clock::now(); });
+
+ QChronoTimer oneNSecTimer{1ns};
+ oneNSecTimer.setTimerType(Qt::PreciseTimer);
+ oneNSecTimer.setSingleShot(true);
+ connect(&oneNSecTimer, &QChronoTimer::timeout, this, [&t2, &loop] {
+ t2 = steady_clock::now();
+ loop.quit();
+ });
-void tst_QTimer::singleShot_static()
-{
- QCoreApplication::processEvents();
- QCOMPARE(s_staticSingleShotUser->helper.calls, s_staticSingleShotUser->calls());
+ zeroTimer.start();
+ oneNSecTimer.start();
+ loop.exec();
+ QCOMPARE_GT(t2, t1);
+ // qDebug() << "t2 - t1" << duration<double, std::chrono::milliseconds::period>{t2 - t1};
}
-QTEST_MAIN(tst_QTimer)
+QTEST_MAIN(tst_QChronoTimer)
-#include "tst_qtimer.moc"
+#include "tst_qchronotimer.moc"
diff --git a/tests/auto/gui/kernel/CMakeLists.txt b/tests/auto/gui/kernel/CMakeLists.txt
index 5377a04a64..9acd817610 100644
--- a/tests/auto/gui/kernel/CMakeLists.txt
+++ b/tests/auto/gui/kernel/CMakeLists.txt
@@ -10,6 +10,7 @@ add_subdirectory(qcursor)
add_subdirectory(qdrag)
add_subdirectory(qevent)
add_subdirectory(qfileopenevent)
+add_subdirectory(qguichronotimer)
add_subdirectory(qguieventdispatcher)
add_subdirectory(qguitimer)
if(NOT ANDROID AND NOT WASM)
diff --git a/tests/auto/gui/kernel/qguichronotimer/CMakeLists.txt b/tests/auto/gui/kernel/qguichronotimer/CMakeLists.txt
index bc292e133b..37848d8cec 100644
--- a/tests/auto/gui/kernel/qguichronotimer/CMakeLists.txt
+++ b/tests/auto/gui/kernel/qguichronotimer/CMakeLists.txt
@@ -2,19 +2,19 @@
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
-## tst_qguitimer Test:
+## tst_qguichronotimer Test:
#####################################################################
if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
- project(tst_qguitimer LANGUAGES CXX)
+ project(tst_qguichronotimer LANGUAGES CXX)
find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
endif()
-function(addGuiTimerTest test)
+function(addGuiChronoTimerTest test)
qt_internal_add_test(${test}
SOURCES
- ../../../corelib/kernel/qtimer/tst_qtimer.cpp
+ ../../../corelib/kernel/qchronotimer/tst_qchronotimer.cpp
LIBRARIES
Qt::CorePrivate
Qt::Gui
@@ -22,17 +22,17 @@ function(addGuiTimerTest test)
)
endfunction()
-addGuiTimerTest(tst_qguitimer)
-qt_internal_extend_target(tst_qguitimer
+addGuiChronoTimerTest(tst_qguichronotimer)
+qt_internal_extend_target(tst_qguichronotimer
DEFINES
- tst_Qtimer=tst_QGuiTimer
+ tst_Qtimer=tst_QGuiChronoTimer
)
if(QT_FEATURE_glib AND UNIX)
- addGuiTimerTest(tst_qguitimer_no_glib)
- qt_internal_extend_target(tst_qguitimer_no_glib
+ addGuiChronoTimerTest(tst_qguichronotimer_no_glib)
+ qt_internal_extend_target(tst_qguichronotimer_no_glib
DEFINES
DISABLE_GLIB
- tst_QTimer=tst_QGuiTimer_no_glib # Class name in the unittest
+ tst_QTimer=tst_QGuiChronoTimer_no_glib # Class name in the unittest
)
endif()