diff --git a/src/corelib/CMakeLists.txt b/src/corelib/CMakeLists.txt index e466c943a08..de28a6183e9 100644 --- a/src/corelib/CMakeLists.txt +++ b/src/corelib/CMakeLists.txt @@ -176,10 +176,10 @@ qt_internal_add_module(Core kernel/qpropertyprivate.h kernel/qsequentialiterable.cpp kernel/qsequentialiterable.h kernel/qsignalmapper.cpp kernel/qsignalmapper.h + kernel/qsingleshottimer.cpp kernel/qsingleshottimer_p.h kernel/qsocketnotifier.cpp kernel/qsocketnotifier.h kernel/qsystemerror.cpp kernel/qsystemerror_p.h kernel/qtestsupport_core.cpp kernel/qtestsupport_core.h - kernel/qsingleshottimer_p.h kernel/qtimer.cpp kernel/qtimer.h kernel/qtimer_p.h kernel/qtranslator.cpp kernel/qtranslator.h kernel/qtranslator_p.h kernel/qvariant.cpp kernel/qvariant.h kernel/qvariant_p.h diff --git a/src/corelib/kernel/qsingleshottimer.cpp b/src/corelib/kernel/qsingleshottimer.cpp new file mode 100644 index 00000000000..ee2b2dc3807 --- /dev/null +++ b/src/corelib/kernel/qsingleshottimer.cpp @@ -0,0 +1,89 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// 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 + +#include "qabstracteventdispatcher.h" +#include "qcoreapplication.h" +#include "qmetaobject_p.h" +#include "private/qnumeric_p.h" + +#include + +QT_BEGIN_NAMESPACE + +QSingleShotTimer::QSingleShotTimer(Duration interval, Qt::TimerType timerType, + const QObject *r, const char *member) + : QObject(QAbstractEventDispatcher::instance()) +{ + connect(this, SIGNAL(timeout()), r, member); + startTimerForReceiver(interval, timerType, r); +} + +QSingleShotTimer::QSingleShotTimer(Duration interval, Qt::TimerType timerType, + const QObject *r, QtPrivate::QSlotObjectBase *slotObj) + : QObject(QAbstractEventDispatcher::instance()) +{ + int signal_index = QMetaObjectPrivate::signalOffset(&staticMetaObject); + Q_ASSERT(QMetaObjectPrivate::signal(&staticMetaObject, signal_index).name() == "timeout"); + QObjectPrivate::connectImpl(this, signal_index, r ? r : this, nullptr, slotObj, + Qt::AutoConnection, nullptr, &staticMetaObject); + + startTimerForReceiver(interval, timerType, r); +} + +QSingleShotTimer::~QSingleShotTimer() +{ + if (timerId > Qt::TimerId::Invalid) + killTimer(timerId); +} + +/* + Move the timer, and the dispatching and handling of the timer event, into + the same thread as where it will be handled, so that it fires reliably even + if the thread that set up the timer is busy. +*/ +void QSingleShotTimer::startTimerForReceiver(Duration interval, Qt::TimerType timerType, + const QObject *receiver) +{ + if (receiver && receiver->thread() != thread()) { + // Avoid leaking the QSingleShotTimer instance in case the application exits before the + // timer fires + connect(QCoreApplication::instance(), &QCoreApplication::aboutToQuit, this, + &QObject::deleteLater); + setParent(nullptr); + moveToThread(receiver->thread()); + + QDeadlineTimer deadline(interval, timerType); + auto invokable = [this, deadline, timerType] { + if (deadline.hasExpired()) { + Q_EMIT timeout(); + } else { + timerId = Qt::TimerId{startTimer(deadline.remainingTimeAsDuration(), timerType)}; + } + }; + QMetaObject::invokeMethod(this, invokable, Qt::QueuedConnection); + } else { + timerId = Qt::TimerId{startTimer(interval, timerType)}; + } +} + +void QSingleShotTimer::timerEvent(QTimerEvent *) +{ + // need to kill the timer _before_ we emit timeout() in case the + // slot connected to timeout calls processEvents() + if (timerId > Qt::TimerId::Invalid) + killTimer(std::exchange(timerId, Qt::TimerId::Invalid)); + + Q_EMIT timeout(); + + // we would like to use delete later here, but it feels like a + // waste to post a new event to handle this event, so we just unset the flag + // and explicitly delete... + delete this; +} + +QT_END_NAMESPACE + +#include "moc_qsingleshottimer_p.cpp" diff --git a/src/corelib/kernel/qsingleshottimer_p.h b/src/corelib/kernel/qsingleshottimer_p.h index dd1402f63a1..44d00252db9 100644 --- a/src/corelib/kernel/qsingleshottimer_p.h +++ b/src/corelib/kernel/qsingleshottimer_p.h @@ -31,6 +31,7 @@ class QSingleShotTimer : public QObject Qt::TimerId timerId = Qt::TimerId::Invalid; +#define inline public: // use the same duration type using Duration = QAbstractEventDispatcher::Duration; @@ -63,79 +64,9 @@ Q_SIGNALS: private: inline void timerEvent(QTimerEvent *) override; +#undef inline }; -QSingleShotTimer::QSingleShotTimer(Duration interval, Qt::TimerType timerType, - const QObject *r, const char *member) - : QObject(QAbstractEventDispatcher::instance()) -{ - connect(this, SIGNAL(timeout()), r, member); - startTimerForReceiver(interval, timerType, r); -} - -QSingleShotTimer::QSingleShotTimer(Duration interval, Qt::TimerType timerType, - const QObject *r, QtPrivate::QSlotObjectBase *slotObj) - : QObject(QAbstractEventDispatcher::instance()) -{ - int signal_index = QMetaObjectPrivate::signalOffset(&staticMetaObject); - Q_ASSERT(QMetaObjectPrivate::signal(&staticMetaObject, signal_index).name() == "timeout"); - QObjectPrivate::connectImpl(this, signal_index, r ? r : this, nullptr, slotObj, - Qt::AutoConnection, nullptr, &staticMetaObject); - - startTimerForReceiver(interval, timerType, r); -} - -QSingleShotTimer::~QSingleShotTimer() -{ - if (timerId > Qt::TimerId::Invalid) - killTimer(timerId); -} - -/* - Move the timer, and the dispatching and handling of the timer event, into - the same thread as where it will be handled, so that it fires reliably even - if the thread that set up the timer is busy. -*/ -void QSingleShotTimer::startTimerForReceiver(Duration interval, Qt::TimerType timerType, - const QObject *receiver) -{ - if (receiver && receiver->thread() != thread()) { - // Avoid leaking the QSingleShotTimer instance in case the application exits before the - // timer fires - connect(QCoreApplication::instance(), &QCoreApplication::aboutToQuit, this, - &QObject::deleteLater); - setParent(nullptr); - moveToThread(receiver->thread()); - - QDeadlineTimer deadline(interval, timerType); - auto invokable = [this, deadline, timerType] { - if (deadline.hasExpired()) { - Q_EMIT timeout(); - } else { - timerId = Qt::TimerId{startTimer(deadline.remainingTimeAsDuration(), timerType)}; - } - }; - QMetaObject::invokeMethod(this, invokable, Qt::QueuedConnection); - } else { - timerId = Qt::TimerId{startTimer(interval, timerType)}; - } -} - -void QSingleShotTimer::timerEvent(QTimerEvent *) -{ - // need to kill the timer _before_ we emit timeout() in case the - // slot connected to timeout calls processEvents() - if (timerId > Qt::TimerId::Invalid) - killTimer(std::exchange(timerId, Qt::TimerId::Invalid)); - - Q_EMIT timeout(); - - // we would like to use delete later here, but it feels like a - // waste to post a new event to handle this event, so we just unset the flag - // and explicitly delete... - delete this; -} - QT_END_NAMESPACE #endif // QSINGLESHOTTIMER_P_H