Short-live QAbstractEventDispatcherV2

This class is a temporary hack to enable transition to an API based on
std::chrono for the Qt event dispatcher. In Qt 7, it will be merged with
QAbstractEventDispatcher, replacing the pure virtuals there with the
ones defined here.

The new API differs from V1 in the following ways:
 - uses Qt::TimerId instead of int to identify timer IDs, so we can't
   accidentally confuse them with something else
 - uses Duration (nanoseconds) to specify the interval, instead of a mix
   of int and qint64
 - add the missing const to remainingTime()
 - rename registeredTimers() to timersForObject() (I'd have kept the
   original name but we can't overload the name if the parameters are
   exactly the same; we could have used QT6_DECL_NEW_OVERLOAD_TAIL, but
   I think the new name is actually better)

Because the old API was mixing int and qint64, we didn't officially
support any timer for more than 2^31 ms (~24.85 days). This should
extend the valid range to 292 years once the dispatchers are ported
over.

Change-Id: I83dda2d36c904517b3c0fffd17b3a7e0afef4b59
Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
Reviewed-by: Ahmad Samir <a.samirh78@gmail.com>
bb10
Thiago Macieira 2024-02-13 22:47:47 -08:00
parent d18d8a7ad1
commit af6afad3b3
3 changed files with 279 additions and 13 deletions

View File

@ -9,9 +9,12 @@
#include <private/qthread_p.h>
#include <private/qcoreapplication_p.h>
#include <private/qfreelist_p.h>
#include <private/qnumeric_p.h>
QT_BEGIN_NAMESPACE
using namespace std::chrono_literals;
// we allow for 2^24 = 8^8 = 16777216 simultaneously running timers
struct QtTimerIdFreeListConstants : public QFreeListDefaultConstants
{
@ -52,6 +55,22 @@ Q_CONSTINIT const int QtTimerIdFreeListConstants::Sizes[QtTimerIdFreeListConstan
typedef QFreeList<void, QtTimerIdFreeListConstants> QtTimerIdFreeList;
Q_GLOBAL_STATIC(QtTimerIdFreeList, timerIdFreeList)
template <typename T> static T fromDuration(std::chrono::nanoseconds interval)
{
using namespace std::chrono;
qint64 value = ceil<milliseconds>(interval).count();
return qt_saturate<T>(value);
}
#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
static inline QAbstractEventDispatcherV2 *v2(QAbstractEventDispatcher *self)
{
if (QAbstractEventDispatcherPrivate::get(self)->isV2)
return static_cast<QAbstractEventDispatcherV2 *>(self);
return nullptr;
}
#endif // Qt 7
QAbstractEventDispatcherPrivate::QAbstractEventDispatcherPrivate()
{
// Create the timer ID free list here to make sure that it is destroyed
@ -121,6 +140,15 @@ void QAbstractEventDispatcherPrivate::releaseTimerId(int timerId)
\sa QEventLoop, QCoreApplication, QThread
*/
/*!
\typedef QAbstractEventDispatcher::Duration
A \c{std::chrono::duration} type that is used in various API in this class.
This type exists to facilitate a possible transition to a higher or lower
granularity.
In all current platforms, it is \c nanoseconds.
*/
/*!
Constructs a new event dispatcher with the given \a parent.
@ -214,16 +242,39 @@ QAbstractEventDispatcher *QAbstractEventDispatcher::instance(QThread *thread)
*/
/*!
\obsolete [6.8] This function will be removed in Qt 7. Use the overload taking \l Duration.
Registers a timer with the specified \a interval and \a timerType for the
given \a object and returns the timer id.
*/
int QAbstractEventDispatcher::registerTimer(qint64 interval, Qt::TimerType timerType, QObject *object)
{
int id = QAbstractEventDispatcherPrivate::allocateTimerId();
return int(registerTimer(interval * 1ms, timerType, object));
}
/*!
\since 6.8
\overload
Registers a timer with the specified \a interval and \a timerType for the
given \a object and returns the timer id.
*/
Qt::TimerId QAbstractEventDispatcher::registerTimer(Duration interval, Qt::TimerType timerType,
QObject *object)
{
auto id = Qt::TimerId(QAbstractEventDispatcherPrivate::allocateTimerId());
#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
if (QAbstractEventDispatcherV2 *self = v2(this))
self->registerTimer(id, interval, timerType, object);
else
registerTimer(qToUnderlying(id), fromDuration<qint64>(interval), timerType, object);
#else
registerTimer(id, interval, timerType, object);
#endif
return id;
}
#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
/*!
\fn void QAbstractEventDispatcher::registerTimer(int timerId, qint64 interval, Qt::TimerType timerType, QObject *object)
@ -240,15 +291,6 @@ int QAbstractEventDispatcher::registerTimer(qint64 interval, Qt::TimerType timer
\sa registerTimer(), unregisterTimers()
*/
/*!
\fn bool QAbstractEventDispatcher::unregisterTimers(QObject *object)
Unregisters all the timers associated with the given \a object.
Returns \c true if all timers were successful removed; otherwise returns \c false.
\sa unregisterTimer(), registeredTimers()
*/
/*!
\fn QList<TimerInfo> QAbstractEventDispatcher::registeredTimers(QObject *object) const
@ -267,6 +309,57 @@ int QAbstractEventDispatcher::registerTimer(qint64 interval, Qt::TimerType timer
\sa Qt::TimerType
*/
#else // Qt 7
/*!
\fn void QAbstractEventDispatcher::registerTimer(Qt::TimerId timerId, Duration interval, Qt::TimerType timerType, QObject *object)
\since 6.8
Register a timer with the specified \a timerId, \a interval, and \a
timerType for the given \a object.
\sa unregisterTimer(), timersForObject()
*/
/*!
\fn bool QAbstractEventDispatcher::unregisterTimer(Qt::TimerId timerId)
\since 6.8
Unregisters the timer with the given \a timerId.
Returns \c true if successful; otherwise returns \c false.
\sa registerTimer(), unregisterTimers()
*/
/*!
\fn QList<TimerInfoV2> QAbstractEventDispatcher::timersForObject(QObject *object) const
\since 6.8
Returns a list of registered timers for \a object. The TimerInfoV2 struct has
\c timerId, \c interval, and \c timerType members.
\sa Qt::TimerType, registerTimer(), unregisterTimer()
*/
/*!
\fn QAbstractEventDispatcher::remainingTime(Qt::TimerId timerId) const
Returns the remaining time of the timer with the given \a timerId.
If the timer is inactive, the returned value will be negative. If the timer
is overdue, the returned value will be 0.
\sa Qt::TimerType, registerTimer(), unregisterTimer()
*/
#endif
/*!
\fn bool QAbstractEventDispatcher::unregisterTimers(QObject *object)
Unregisters all the timers associated with the given \a object. Returns \c
true if all timers were successfully removed; otherwise returns \c false.
\sa unregisterTimer(), registeredTimers()
*/
/*! \fn void QAbstractEventDispatcher::wakeUp()
\threadsafe
@ -308,6 +401,7 @@ void QAbstractEventDispatcher::closingDown()
/*!
\class QAbstractEventDispatcher::TimerInfo
\deprecated [6.8] Use TimerInfoV2
\inmodule QtCore
This struct represents information about a timer:
@ -315,7 +409,7 @@ void QAbstractEventDispatcher::closingDown()
\l{QAbstractEventDispatcher::TimerInfo::interval}{interval}, and
\l{QAbstractEventDispatcher::TimerInfo::timerType}{timerType}.
\sa registeredTimers()
\sa registeredTimers(), QAbstractEventDispatcher::TimerInfoV2, timersForObject()
*/
/*! \fn QAbstractEventDispatcher::TimerInfo::TimerInfo(int timerId, int interval, Qt::TimerType timerType)
@ -340,6 +434,37 @@ void QAbstractEventDispatcher::closingDown()
\sa Qt::TimerType
*/
/*!
\class QAbstractEventDispatcher::TimerInfoV2
\inmodule QtCore
This struct represents information about a timer:
\l{QAbstractEventDispatcher::TimerInfoV2::timerId}{timerId},
\l{QAbstractEventDispatcher::TimerInfoV2::interval}{interval}, and
\l{QAbstractEventDispatcher::TimerInfoV2::timerType}{timerType}.
\sa timersForObject()
*/
/*!
\variable QAbstractEventDispatcher::TimerInfoV2::timerId
The timer's unique id. This is created by registerTimer() upon creation and
uniquely identifies a timer while it is active. It is also used by
QTimer::id() and returned by QObject::startTimer().
*/
/*!
\variable QAbstractEventDispatcher::TimerInfoV2::interval
The timer's interval.
*/
/*!
\variable QAbstractEventDispatcher::TimerInfoV2::timerType
The timer's type
\sa Qt::TimerType
*/
/*!
Installs an event filter \a filterObj for all native events received by the application.
@ -452,6 +577,89 @@ bool QAbstractEventDispatcher::filterNativeEvent(const QByteArray &eventType, vo
\sa awake()
*/
#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
/*!
\class QAbstractEventDispatcherV2
\inmodule QtCore
This class is a temporary hack to enable transition to an API based on
\c{std::chrono} for the Qt event dispatcher. In Qt 7, it will be merged
with QAbstractEventDispatcher, replacing the pure virtuals there with the
ones defined here.
It is recommended applications and libraries port to the new API before
that future release to simplify work when the time comes.
*/
/*!
Constructs a new event dispatcher with the given \a parent.
*/
QAbstractEventDispatcherV2::QAbstractEventDispatcherV2(QObject *parent)
: QAbstractEventDispatcherV2(*new QAbstractEventDispatcherPrivate, parent)
{
}
/*!
\internal
*/
QAbstractEventDispatcherV2::QAbstractEventDispatcherV2(QAbstractEventDispatcherPrivate &dd,
QObject *parent)
: QAbstractEventDispatcher((dd.isV2 = true, dd), parent)
{
}
/*!
Destroys the event dispatcher.
*/
QAbstractEventDispatcherV2::~QAbstractEventDispatcherV2() = default;
/*!
\internal
Temporary compatibility override.
*/
void QAbstractEventDispatcherV2::registerTimer(int timerId, qint64 interval,
Qt::TimerType timerType, QObject *object)
{
auto self = static_cast<QAbstractEventDispatcherV2 *>(this);
self->registerTimer(Qt::TimerId(timerId), interval * 1ms, timerType, object);
}
/*!
\internal
Temporary compatibility override.
*/
bool QAbstractEventDispatcherV2::unregisterTimer(int timerId)
{
auto self = static_cast<QAbstractEventDispatcherV2 *>(this);
return self->unregisterTimer(Qt::TimerId(timerId));
}
/*!
\internal
Temporary compatibility override.
*/
auto QAbstractEventDispatcherV2::registeredTimers(QObject *object) const -> QList<TimerInfo>
{
auto self = static_cast<const QAbstractEventDispatcherV2 *>(this);
QList<TimerInfoV2> timers = self->timersForObject(object);
QList<TimerInfo> result;
result.reserve(timers.size());
for (const TimerInfoV2 &t : timers)
result.emplaceBack(qToUnderlying(t.timerId), fromDuration<int>(t.interval), t.timerType);
return result;
}
/*!
\internal
Temporary compatibility override.
*/
int QAbstractEventDispatcherV2::remainingTime(int timerId)
{
auto self = static_cast<QAbstractEventDispatcherV2 *>(this);
return fromDuration<int>(self->remainingTime(Qt::TimerId(timerId)));
}
#endif // ! Qt 7
QT_END_NAMESPACE
#include "moc_qabstracteventdispatcher.cpp"

View File

@ -19,6 +19,7 @@ class Q_CORE_EXPORT QAbstractEventDispatcher : public QObject
Q_DECLARE_PRIVATE(QAbstractEventDispatcher)
public:
using Duration = std::chrono::nanoseconds;
struct TimerInfo
{
int timerId;
@ -28,6 +29,12 @@ public:
inline TimerInfo(int id, int i, Qt::TimerType t)
: timerId(id), interval(i), timerType(t) { }
};
struct TimerInfoV2
{
Duration interval;
Qt::TimerId timerId;
Qt::TimerType timerType;
};
explicit QAbstractEventDispatcher(QObject *parent = nullptr);
~QAbstractEventDispatcher();
@ -39,13 +46,23 @@ public:
virtual void registerSocketNotifier(QSocketNotifier *notifier) = 0;
virtual void unregisterSocketNotifier(QSocketNotifier *notifier) = 0;
Qt::TimerId registerTimer(Duration interval, Qt::TimerType timerType, QObject *object);
#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
int registerTimer(qint64 interval, Qt::TimerType timerType, QObject *object);
// old, integer-based API
virtual void registerTimer(int timerId, qint64 interval, Qt::TimerType timerType, QObject *object) = 0;
virtual bool unregisterTimer(int timerId) = 0;
virtual bool unregisterTimers(QObject *object) = 0;
virtual QList<TimerInfo> registeredTimers(QObject *object) const = 0;
virtual int remainingTime(int timerId) = 0;
#else
virtual void registerTimer(Qt::TimerId timerId, Duration interval, Qt::TimerType timerType, QObject *object) = 0;
virtual bool unregisterTimer(Qt::TimerId timerId) = 0;
virtual QList<TimerInfoV2> timersForObject(QObject *object) const = 0;
virtual Duration remainingTime(Qt::TimerId timerId) const = 0;
#endif
virtual bool unregisterTimers(QObject *object) = 0;
virtual void wakeUp() = 0;
virtual void interrupt() = 0;
@ -67,6 +84,40 @@ protected:
};
Q_DECLARE_TYPEINFO(QAbstractEventDispatcher::TimerInfo, Q_PRIMITIVE_TYPE);
Q_DECLARE_TYPEINFO(QAbstractEventDispatcher::TimerInfoV2, Q_PRIMITIVE_TYPE);
#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
class Q_CORE_EXPORT QAbstractEventDispatcherV2 : public QAbstractEventDispatcher
{
Q_OBJECT
Q_DECLARE_PRIVATE(QAbstractEventDispatcher) // not V2
public:
explicit QAbstractEventDispatcherV2(QObject *parent = nullptr);
~QAbstractEventDispatcherV2();
// new virtuals
virtual void registerTimer(Qt::TimerId timerId, Duration interval, Qt::TimerType timerType,
QObject *object) = 0;
virtual bool unregisterTimer(Qt::TimerId timerId) = 0;
virtual QList<TimerInfoV2> timersForObject(QObject *object) const = 0;
virtual Duration remainingTime(Qt::TimerId timerId) const = 0;
protected:
QAbstractEventDispatcherV2(QAbstractEventDispatcherPrivate &, QObject *parent);
private:
// final overrides from V1
virtual void registerTimer(int timerId, qint64 interval, Qt::TimerType timerType,
QObject *object) override final;
virtual bool unregisterTimer(int timerId) override final;
virtual QList<TimerInfo> registeredTimers(QObject *object) const override final;
virtual int remainingTime(int timerId) override final;
};
#else
using QAbstractEventDispatcherV2 = QAbstractEventDispatcher;
#endif // Qt 7
QT_END_NAMESPACE

View File

@ -33,10 +33,17 @@ public:
QList<QAbstractNativeEventFilter *> eventFilters;
bool isV2 = false;
static int allocateTimerId();
static void releaseTimerId(int id);
static void releaseTimerId(Qt::TimerId id)
{ releaseTimerId(qToUnderlying(id)); }
static QAbstractEventDispatcherPrivate *get(QAbstractEventDispatcher *o)
{ return o->d_func(); }
static const QAbstractEventDispatcherPrivate *get(const QAbstractEventDispatcher *o)
{ return o->d_func(); }
};
QT_END_NAMESPACE