QTimer/QObject::startTimer: improve the detection of overflow
Converting from int milliseconds to int64_t nanoseconds can't overflow
(it won't even for picoseconds, so we'll be fine for a couple more
decades), so we only need to address the cases where the millisecond
value was passed in int64_t: that is, in the std::chrono::milliseconds
overloads. For the other cases, I added a comment.
Amends bfc7535a10 to not allow the
detected overflow to happen at all, which could cause the timer to
become very small. Instead, we saturate to the maximum, which is about
292 years (just under 106752 days). That's longer than computers have
existed, so the chance that some Qt application is still running on a
computer without any reboots from today to 24th century is remote at
best.
This parallels QDeadlineTimer, which already has code to saturate when
using milliseconds.
Change-Id: I6818d78a57394e37857bfffd17b9b1465b6a5d19
Reviewed-by: Ahmad Samir <a.samirh78@gmail.com>
bb10
parent
3abb1e7b54
commit
99f78eb708
|
|
@ -1012,11 +1012,13 @@ int QObject::startTimer(std::chrono::milliseconds time, Qt::TimerType timerType)
|
|||
{
|
||||
using namespace std::chrono;
|
||||
using ratio = std::ratio_divide<std::milli, std::nano>;
|
||||
if (nanoseconds::rep r; qMulOverflow<ratio::num>(time.count(), &r)) {
|
||||
qWarning("QObject::startTimer(std::chrono::milliseconds time ...): "
|
||||
"'time' arg will overflow when converted to nanoseconds.");
|
||||
nanoseconds::rep r;
|
||||
if (qMulOverflow<ratio::num>(time.count(), &r)) {
|
||||
qWarning("QObject::startTimer(std::chrono::milliseconds): "
|
||||
"'time' arg overflowed when converted to nanoseconds.");
|
||||
r = nanoseconds::max().count();
|
||||
}
|
||||
return startTimer(nanoseconds{time}, timerType);
|
||||
return startTimer(nanoseconds{r}, timerType);
|
||||
}
|
||||
|
||||
#if QT_CONFIG(processenvironment)
|
||||
|
|
|
|||
|
|
@ -1816,6 +1816,8 @@ void QObjectPrivate::setThreadData_helper(QThreadData *currentData, QThreadData
|
|||
|
||||
int QObject::startTimer(int interval, Qt::TimerType timerType)
|
||||
{
|
||||
// no overflow can happen here:
|
||||
// 2^31 ms * 1,000,000 always fits a 64-bit signed integer type
|
||||
return startTimer(std::chrono::milliseconds{interval}, timerType);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@
|
|||
#include "qabstracteventdispatcher.h"
|
||||
#include "qcoreapplication.h"
|
||||
#include "qmetaobject_p.h"
|
||||
#include "private/qnumeric_p.h"
|
||||
|
||||
#include <chrono>
|
||||
|
||||
|
|
@ -43,6 +44,20 @@ public:
|
|||
inline void startTimerForReceiver(Duration interval, Qt::TimerType timerType,
|
||||
const QObject *receiver);
|
||||
|
||||
static Duration fromMsecs(std::chrono::milliseconds ms)
|
||||
{
|
||||
using namespace std::chrono;
|
||||
using ratio = std::ratio_divide<std::milli, Duration::period>;
|
||||
static_assert(ratio::den == 1);
|
||||
|
||||
Duration::rep r;
|
||||
if (qMulOverflow<ratio::num>(ms.count(), &r)) {
|
||||
qWarning("QTimer::singleShot(std::chrono::milliseconds, ...): "
|
||||
"interval argument overflowed when converted to nanoseconds.");
|
||||
return Duration::max();
|
||||
}
|
||||
return Duration{r};
|
||||
}
|
||||
Q_SIGNALS:
|
||||
void timeout();
|
||||
|
||||
|
|
|
|||
|
|
@ -213,7 +213,7 @@ void QTimer::start()
|
|||
if (d->isActive()) // stop running timer
|
||||
stop();
|
||||
|
||||
const auto newId = Qt::TimerId{QObject::startTimer(d->inter * 1ms, d->type)};
|
||||
Qt::TimerId newId{ QObject::startTimer(d->inter * 1ms, d->type) }; // overflow impossible
|
||||
if (newId > Qt::TimerId::Invalid) {
|
||||
d->id = newId;
|
||||
d->isActiveData.notify();
|
||||
|
|
@ -332,7 +332,7 @@ void QTimer::singleShotImpl(std::chrono::milliseconds msec, Qt::TimerType timerT
|
|||
return;
|
||||
}
|
||||
|
||||
new QSingleShotTimer(msec, timerType, receiver, slotObj);
|
||||
new QSingleShotTimer(QSingleShotTimer::fromMsecs(msec), timerType, receiver, slotObj);
|
||||
}
|
||||
|
||||
/*!
|
||||
|
|
@ -396,7 +396,7 @@ void QTimer::singleShot(std::chrono::milliseconds msec, Qt::TimerType timerType,
|
|||
Qt::QueuedConnection);
|
||||
return;
|
||||
}
|
||||
(void) new QSingleShotTimer(msec, timerType, receiver, member);
|
||||
(void) new QSingleShotTimer(QSingleShotTimer::fromMsecs(msec), timerType, receiver, member);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -592,7 +592,7 @@ void QTimer::setInterval(std::chrono::milliseconds interval)
|
|||
d->inter.setValueBypassingBindings(msec);
|
||||
if (d->isActive()) { // create new timer
|
||||
QObject::killTimer(d->id); // restart timer
|
||||
const auto newId = Qt::TimerId{QObject::startTimer(msec * 1ms, d->type)};
|
||||
Qt::TimerId newId{ QObject::startTimer(msec * 1ms, d->type) }; // overflow impossible
|
||||
if (newId > Qt::TimerId::Invalid) {
|
||||
// Restarted successfully. No need to update the active state.
|
||||
d->id = newId;
|
||||
|
|
|
|||
|
|
@ -151,7 +151,8 @@ static dbus_bool_t qDBusAddTimeout(DBusTimeout *timeout, void *data)
|
|||
|
||||
Q_ASSERT(d->timeouts.key(timeout, 0) == 0);
|
||||
|
||||
int timerId = d->startTimer(std::chrono::milliseconds{q_dbus_timeout_get_interval(timeout)});
|
||||
using namespace std::chrono_literals;
|
||||
int timerId = d->startTimer(q_dbus_timeout_get_interval(timeout) * 1ms); // no overflow possible
|
||||
if (!timerId)
|
||||
return false;
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue