QDeadlineTimer: use std::chrono::steady_clock everywhere

This matches the work that was done for QElapsedTimer. The
QDeadlineTimer::t2 member is now always 0.

This also removes the last distinction of timer types. Originally I had
intended to use CLOCK_MONOTONIC_COARSE on Linux[1], but that created
more problems than was worth, so I abandoned the idea in 2016.

[1] https://codereview.qt-project.org/c/qt/qtbase/+/159933

Change-Id: Ieec322d73c1e40ad95c8fffd17468b313798ef79
Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
Reviewed-by: Ahmad Samir <a.samirh78@gmail.com>
bb10
Thiago Macieira 2023-02-22 20:18:28 -08:00
parent 5c95cb8722
commit b498e1ae3a
5 changed files with 36 additions and 162 deletions

View File

@ -155,7 +155,7 @@ qt_internal_add_module(Core
kernel/qcoreapplication_platform.h
kernel/qcorecmdlineargs_p.h
kernel/qcoreevent.cpp kernel/qcoreevent.h
kernel/qdeadlinetimer.cpp kernel/qdeadlinetimer.h kernel/qdeadlinetimer_p.h
kernel/qdeadlinetimer.cpp kernel/qdeadlinetimer.h
kernel/qelapsedtimer.cpp kernel/qelapsedtimer.h
kernel/qeventloop.cpp kernel/qeventloop.h kernel/qeventloop_p.h
kernel/qfunctions_p.h

View File

@ -2,13 +2,8 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qdeadlinetimer.h"
#include "qdeadlinetimer_p.h"
#include "private/qnumeric_p.h"
#ifdef Q_OS_UNIX
# include "qcore_unix_p.h"
#endif
QT_BEGIN_NAMESPACE
QT_IMPL_METATYPE_EXTERN(QDeadlineTimer)
@ -111,15 +106,18 @@ bool TimeReference::sign(qint64 secs, qint64 nsecs)
return nsecs > 0;
}
#if defined(Q_OS_UNIX)
inline bool TimeReference::addNanoseconds(qint64 arg)
{
return addSecsAndNSecs(arg / giga, arg % giga);
return adjust(arg, 0);
}
inline bool TimeReference::addMilliseconds(qint64 arg)
{
return addSecsAndNSecs(arg / kilo, (arg % kilo) * mega);
static constexpr qint64 maxMilliseconds = Max / mega;
if (qAbs(arg) > maxMilliseconds)
return false;
return addNanoseconds(arg * mega);
}
/*!
@ -134,20 +132,14 @@ inline bool TimeReference::addMilliseconds(qint64 arg)
*
* Returns true if operation was successful, false on over|underflow
*/
bool TimeReference::addSecsAndNSecs(qint64 addSecs, qint64 addNSecs)
inline bool TimeReference::addSecsAndNSecs(qint64 addSecs, qint64 addNSecs)
{
// Normalize the arguments
if (qAbs(addNSecs) >= giga) {
if (add_overflow<qint64>(addSecs, addNSecs / giga, &addSecs))
return false;
static constexpr qint64 maxSeconds = Max / giga;
static constexpr qint64 minSeconds = Min / giga;
if (addSecs > maxSeconds || addSecs < minSeconds || add_overflow<qint64>(addSecs * giga, addNSecs, &addNSecs))
return false;
addNSecs %= giga;
}
if (addNSecs < 0)
return adjust(addSecs, ugiga - unsigned(-addNSecs), -1);
return adjust(addSecs, unsigned(addNSecs));
return addNanoseconds(addNSecs);
}
/*!
@ -158,21 +150,12 @@ bool TimeReference::addSecsAndNSecs(qint64 addSecs, qint64 addNSecs)
*
* Returns true if operation was successful, false on over|underflow
*/
bool TimeReference::adjust(const qint64 t1, const unsigned t2, qint64 carrySeconds)
inline bool TimeReference::adjust(const qint64 t1, const unsigned t2, qint64 carrySeconds)
{
static_assert(QDeadlineTimerNanosecondsInT2);
nsecs += t2;
if (nsecs >= ugiga) {
nsecs -= ugiga;
carrySeconds++;
}
Q_UNUSED(t2);
Q_UNUSED(carrySeconds);
// We don't worry about the order of addition, because the result returned by
// callers of this function is unchanged regardless of us over|underflowing.
// If we do, we do so by no more than a second, thus saturating the timer to
// Forever has the same effect as if we did the arithmetic exactly and salvaged
// the overflow.
return !add_overflow<qint64>(secs, t1, &secs) && !add_overflow<qint64>(secs, carrySeconds, &secs);
return !add_overflow<qint64>(secs, t1, &secs);
}
/*!
@ -189,8 +172,9 @@ bool TimeReference::adjust(const qint64 t1, const unsigned t2, qint64 carrySecon
*/
inline bool TimeReference::subtract(const qint64 t1, const unsigned t2)
{
Q_ASSERT(t2 < ugiga);
return adjust(-t1, ugiga - t2, -1);
Q_UNUSED(t2);
return addNanoseconds(-t1);
}
/*!
@ -203,78 +187,6 @@ inline bool TimeReference::subtract(const qint64 t1, const unsigned t2)
*
* Returns true if operation was successful, false on over|underflow
*/
inline bool TimeReference::toMilliseconds(qint64 *result, RoundingStrategy rounding) const
{
static constexpr qint64 maxSeconds = Max / kilo;
static constexpr qint64 minSeconds = Min / kilo;
if (secs > maxSeconds || secs < minSeconds)
return false;
unsigned ns = rounding == RoundDown ? nsecs : nsecs + umega - 1;
return !add_overflow<qint64>(secs * kilo, ns / umega, result);
}
/*!
* \internal
*
* Converts the time reference to nanoseconds.
*
* Checks are done without making use of mul_overflow because it may
* not be implemented on some 32bit platforms.
*
* Returns true if operation was successful, false on over|underflow
*/
inline bool TimeReference::toNanoseconds(qint64 *result) const
{
static constexpr qint64 maxSeconds = Max / giga;
static constexpr qint64 minSeconds = Min / giga;
if (secs > maxSeconds || secs < minSeconds)
return false;
return !add_overflow<qint64>(secs * giga, nsecs, result);
}
#else
inline bool TimeReference::addNanoseconds(qint64 arg)
{
return adjust(arg, 0);
}
inline bool TimeReference::addMilliseconds(qint64 arg)
{
static constexpr qint64 maxMilliseconds = Max / mega;
if (qAbs(arg) > maxMilliseconds)
return false;
return addNanoseconds(arg * mega);
}
inline bool TimeReference::addSecsAndNSecs(qint64 addSecs, qint64 addNSecs)
{
static constexpr qint64 maxSeconds = Max / giga;
static constexpr qint64 minSeconds = Min / giga;
if (addSecs > maxSeconds || addSecs < minSeconds || add_overflow<qint64>(addSecs * giga, addNSecs, &addNSecs))
return false;
return addNanoseconds(addNSecs);
}
inline bool TimeReference::adjust(const qint64 t1, const unsigned t2, qint64 carrySeconds)
{
static_assert(!QDeadlineTimerNanosecondsInT2);
Q_UNUSED(t2);
Q_UNUSED(carrySeconds);
return !add_overflow<qint64>(secs, t1, &secs);
}
inline bool TimeReference::subtract(const qint64 t1, const unsigned t2)
{
Q_UNUSED(t2);
return addNanoseconds(-t1);
}
inline bool TimeReference::toMilliseconds(qint64 *result, RoundingStrategy rounding) const
{
// Force QDeadlineTimer to treat the border cases as
@ -290,12 +202,21 @@ inline bool TimeReference::toMilliseconds(qint64 *result, RoundingStrategy round
return true;
}
/*!
* \internal
*
* Converts the time reference to nanoseconds.
*
* Checks are done without making use of mul_overflow because it may
* not be implemented on some 32bit platforms.
*
* Returns true if operation was successful, false on over|underflow
*/
inline bool TimeReference::toNanoseconds(qint64 *result) const
{
*result = secs;
return true;
}
#endif
/*!
\class QDeadlineTimer
@ -842,18 +763,12 @@ QDeadlineTimer QDeadlineTimer::addNSecs(QDeadlineTimer dt, qint64 nsecs) noexcep
*/
QDeadlineTimer QDeadlineTimer::current(Qt::TimerType timerType) noexcept
{
QDeadlineTimer result;
#ifdef Q_OS_UNIX
static_assert(QDeadlineTimerNanosecondsInT2);
timespec ts = qt_gettime();
result.t1 = ts.tv_sec;
result.t2 = ts.tv_nsec;
#else
// ensure we get nanoseconds; this will work so long as steady_clock's
// time_point isn't of finer resolution (picoseconds)
std::chrono::nanoseconds ns = std::chrono::steady_clock::now().time_since_epoch();
QDeadlineTimer result;
result.t1 = ns.count();
#endif
result.type = timerType;
return result;
}

View File

@ -148,10 +148,6 @@ private:
qint64 rawRemainingTimeNSecs() const noexcept;
};
#if defined(Q_OS_DARWIN) || defined(Q_OS_LINUX) || (defined(Q_CC_MSVC) && Q_CC_MSVC >= 1900)
// We know for these OS/compilers that the std::chrono::steady_clock uses the same
// reference time as QDeadlineTimer
template <> inline std::chrono::steady_clock::time_point
QDeadlineTimer::deadline<std::chrono::steady_clock, std::chrono::steady_clock::duration>() const
{
@ -174,7 +170,6 @@ QDeadlineTimer::setDeadline<std::chrono::steady_clock, std::chrono::steady_clock
type_);
}
}
#endif
Q_DECLARE_SHARED(QDeadlineTimer)

View File

@ -1,34 +0,0 @@
// 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 QDEADLINETIMER_P_H
#define QDEADLINETIMER_P_H
//
// W A R N I N G
// -------------
//
// This file is not part of the Qt API. It exists purely as an
// implementation detail. This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//
#include <QtCore/private/qglobal_p.h>
QT_BEGIN_NAMESPACE
enum {
#if defined(Q_OS_UNIX)
// t1 contains seconds and t2 contains nanoseconds
QDeadlineTimerNanosecondsInT2 = 1
#else
// t1 contains nanoseconds, t2 is always zero
QDeadlineTimerNanosecondsInT2 = 0
#endif
};
QT_END_NAMESPACE
#endif

View File

@ -520,14 +520,15 @@ void tst_QDeadlineTimer::overflow()
// Make sure setRemainingTime underflows gracefully
deadline.setPreciseRemainingTime(std::numeric_limits<qint64>::min() / 10, 0, timerType);
QVERIFY(!deadline.isForever()); // On Win/macOS the above underflows, make sure we don't saturate to Forever
QVERIFY(!deadline.isForever()); // The above underflows, so make sure we don't saturate to Forever
QCOMPARE(deadline.remainingTimeNSecs(), 0);
QVERIFY(deadline.remainingTime() == 0);
// If the timer is saturated we don't want to get a valid number of milliseconds
QVERIFY(deadline.deadline() == std::numeric_limits<qint64>::min());
// Check that the conversion to milliseconds and nanoseconds underflows gracefully
deadline.setPreciseDeadline(std::numeric_limits<qint64>::min() / 10, 0, timerType);
QVERIFY(!deadline.isForever()); // On Win/macOS the above underflows, make sure we don't saturate to Forever
QVERIFY(!deadline.isForever()); // The above underflows, make sure we don't saturate to Forever
QVERIFY(deadline.deadline() == std::numeric_limits<qint64>::min());
QVERIFY(deadline.deadlineNSecs() == std::numeric_limits<qint64>::min());
}
@ -630,16 +631,13 @@ void tst_QDeadlineTimer::stdchrono()
QTRY_VERIFY2_WITH_TIMEOUT(timersExecuted,
"Looks like timers didn't fire on time.", 4 * minResolution);
#if defined(Q_OS_DARWIN) || defined(Q_OS_LINUX) || (defined(Q_CC_MSVC) && Q_CC_MSVC >= 1900)
{
// We know for these OS/compilers that the std::chrono::steady_clock uses the same
// reference time as QDeadlineTimer
qint64 before = duration_cast<nanoseconds>(steady_before.time_since_epoch()).count();
qint64 after = duration_cast<nanoseconds>(steady_after.time_since_epoch()).count();
QCOMPARE_GT(now.deadlineNSecs(), before);
QCOMPARE_LT(now.deadlineNSecs(), after);
}
#endif
{
auto diff = duration_cast<milliseconds>(steady_after - steady_deadline);
QCOMPARE_GT(diff.count(), minResolution / 2);