diff options
Diffstat (limited to 'src/corelib/kernel/qchronotimer.cpp')
| -rw-r--r-- | src/corelib/kernel/qchronotimer.cpp | 645 |
1 files changed, 224 insertions, 421 deletions
diff --git a/src/corelib/kernel/qchronotimer.cpp b/src/corelib/kernel/qchronotimer.cpp index cbeeb979343..d67d7ca48fd 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" |
