diff options
author | Thiago Macieira <thiago.macieira@intel.com> | 2023-02-22 21:08:01 -0800 |
---|---|---|
committer | Thiago Macieira <thiago.macieira@intel.com> | 2023-04-18 19:23:45 -0300 |
commit | 39be743e5cde56f6a8af55475ed39f53c6d02a51 (patch) | |
tree | 6f0f1d535da36aa282cc47bf7f6b3f1eb0c6a840 | |
parent | 7ba76731ef70a54de841f5310f17cb48977c67d8 (diff) |
QDeadlineTimer: remove internal TimeReference class
This removes all uses of QDeadlineTimer::t2 member in the .cpp (so it
gets marked [[maybe_unused]]) and greatly simplifies the code.
Change-Id: Ieec322d73c1e40ad95c8fffd17465bd50c1113ea
Reviewed-by: Ahmad Samir <a.samirh78@gmail.com>
Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
-rw-r--r-- | src/corelib/kernel/qdeadlinetimer.cpp | 316 | ||||
-rw-r--r-- | tests/auto/corelib/kernel/qdeadlinetimer/tst_qdeadlinetimer.cpp | 2 |
2 files changed, 47 insertions, 271 deletions
diff --git a/src/corelib/kernel/qdeadlinetimer.cpp b/src/corelib/kernel/qdeadlinetimer.cpp index eab58df2e6..25af463f8e 100644 --- a/src/corelib/kernel/qdeadlinetimer.cpp +++ b/src/corelib/kernel/qdeadlinetimer.cpp @@ -8,214 +8,38 @@ QT_BEGIN_NAMESPACE QT_IMPL_METATYPE_EXTERN(QDeadlineTimer) -namespace { - class TimeReference - { - enum : unsigned { - umega = 1000 * 1000, - ugiga = umega * 1000 - }; - - enum : qint64 { - kilo = 1000, - mega = kilo * 1000, - giga = mega * 1000 - }; - - public: - enum RoundingStrategy { - RoundDown, - RoundUp, - RoundDefault = RoundDown - }; - - static constexpr qint64 Min = std::numeric_limits<qint64>::min(); - static constexpr qint64 Max = std::numeric_limits<qint64>::max(); - - inline TimeReference(qint64 = 0, unsigned = 0); - inline void updateTimer(qint64 &, unsigned &); - - inline bool addNanoseconds(qint64); - inline bool addMilliseconds(qint64); - bool addSecsAndNSecs(qint64, qint64); - - inline bool subtract(const qint64, const unsigned); - - inline bool toMilliseconds(qint64 *, RoundingStrategy = RoundDefault) const; - inline bool toNanoseconds(qint64 *) const; - - inline void saturate(bool toMax); - static bool sign(qint64, qint64); - - private: - bool adjust(const qint64, const unsigned, qint64 = 0); - - private: - qint64 secs; - unsigned nsecs; - }; -} - -inline TimeReference::TimeReference(qint64 t1, unsigned t2) - : secs(t1), nsecs(t2) -{ -} +using namespace std::chrono; -inline void TimeReference::updateTimer(qint64 &t1, unsigned &t2) -{ - t1 = secs; - t2 = nsecs; -} - -inline void TimeReference::saturate(bool toMax) +namespace { +struct TimeReference : std::numeric_limits<qint64> { - secs = toMax ? Max : Min; + static constexpr qint64 Min = min(); + static constexpr qint64 Max = max(); +}; } -/*! - * \internal - * - * Determines the sign of a (seconds, nanoseconds) pair - * for differentiating overflow from underflow. It doesn't - * deal with equality as it shouldn't ever be called in that case. - * - * Returns true if the pair represents a positive time offset - * false otherwise. - */ -bool TimeReference::sign(qint64 secs, qint64 nsecs) +template <typename Duration1, typename... Durations> +static qint64 add_saturate(qint64 t1, Duration1 dur, Durations... extra) { - if (secs > 0) { - if (nsecs > 0) - return true; - } else { - if (nsecs < 0) - return false; + qint64 v = dur.count(); + qint64 saturated = std::numeric_limits<qint64>::max(); + if (v < 0) + saturated = std::numeric_limits<qint64>::min(); + + // convert to nanoseconds with saturation + using Ratio = std::ratio_divide<typename Duration1::period, nanoseconds::period>; + static_assert(Ratio::den == 1, "sub-multiples of nanosecond are not supported"); + if (qMulOverflow<Ratio::num>(v, &v)) + return saturated; + + qint64 r; + if (qAddOverflow(t1, v, &r)) + return saturated; + if constexpr (sizeof...(Durations)) { + // chain more additions + return add_saturate(r, extra...); } - - // They are different in sign - secs += nsecs / giga; - if (secs > 0) - return true; - else if (secs < 0) - return false; - - // We should never get over|underflow out of - // the case: secs * giga == -nsecs - // So the sign of nsecs is the deciding factor - Q_ASSERT(nsecs % giga != 0); - return nsecs > 0; -} - -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); -} - -/*! - * \internal - * - * Adds \a t1 addSecs seconds and \a addNSecs nanoseconds to the - * time reference. The arguments are normalized to seconds (qint64) - * and nanoseconds (unsigned) before the actual calculation is - * delegated to adjust(). If the nanoseconds are negative the - * owed second used for the normalization is passed on to adjust() - * as third argument. - * - * Returns true if operation was successful, false on over|underflow - */ -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); -} - -/*! - * \internal - * - * Adds \a t1 seconds and \a t2 nanoseconds to the internal members. - * Takes into account the additional \a carrySeconds we may owe or need to carry over. - * - * Returns true if operation was successful, false on over|underflow - */ -inline bool TimeReference::adjust(const qint64 t1, const unsigned t2, qint64 carrySeconds) -{ - Q_UNUSED(t2); - Q_UNUSED(carrySeconds); - - return !add_overflow<qint64>(secs, t1, &secs); -} - -/*! - * \internal - * - * Subtracts \a t1 seconds and \a t2 nanoseconds from the time reference. - * When normalizing the nanoseconds to a positive number the owed seconds is - * passed as third argument to adjust() as the seconds may over|underflow - * if we do the calculation directly. There is little sense to check the - * seconds for over|underflow here in case we are going to need to carry - * over a second _after_ we add the nanoseconds. - * - * Returns true if operation was successful, false on over|underflow - */ -inline bool TimeReference::subtract(const qint64 t1, const unsigned t2) -{ - Q_UNUSED(t2); - - return addNanoseconds(-t1); -} - -/*! - * \internal - * - * Converts the time reference to milliseconds. - * - * 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::toMilliseconds(qint64 *result, RoundingStrategy rounding) const -{ - // Force QDeadlineTimer to treat the border cases as - // over|underflow and saturate the results returned to the user. - // We don't want to get valid milliseconds out of saturated timers. - if (secs == Max || secs == Min) - return false; - - *result = secs / mega; - if (rounding == RoundUp && secs > *result * mega) - (*result)++; - - 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; + return r; } /*! @@ -435,10 +259,8 @@ void QDeadlineTimer::setRemainingTime(qint64 msecs, Qt::TimerType timerType) noe *this = current(timerType); - TimeReference ref(t1, t2); - if (!ref.addMilliseconds(msecs)) - ref.saturate(msecs > 0); - ref.updateTimer(t1, t2); + milliseconds ms(msecs); + t1 = add_saturate(t1, ms); } /*! @@ -460,10 +282,7 @@ void QDeadlineTimer::setPreciseRemainingTime(qint64 secs, qint64 nsecs, Qt::Time } *this = current(timerType); - TimeReference ref(t1, t2); - if (!ref.addSecsAndNSecs(secs, nsecs)) - ref.saturate(TimeReference::sign(secs, nsecs)); - ref.updateTimer(t1, t2); + t1 = add_saturate(t1, seconds{secs}, nanoseconds{nsecs}); } /*! @@ -561,19 +380,8 @@ qint64 QDeadlineTimer::remainingTime() const noexcept if (isForever()) return -1; - QDeadlineTimer now = current(timerType()); - TimeReference ref(t1, t2); - - qint64 msecs; - if (!ref.subtract(now.t1, now.t2)) - return 0; // We can only underflow here - - // If we fail the conversion, t1 < now.t1 means we underflowed, - // thus the deadline had long expired - if (!ref.toMilliseconds(&msecs, TimeReference::RoundUp)) - return t1 < now.t1 ? 0 : -1; - - return msecs < 0 ? 0 : msecs; + nanoseconds nsecs(remainingTimeNSecs()); + return ceil<milliseconds>(nsecs).count(); } /*! @@ -595,23 +403,16 @@ qint64 QDeadlineTimer::remainingTimeNSecs() const noexcept /*! \internal Same as remainingTimeNSecs, but may return negative remaining times. Does - not deal with Forever. In case of underflow the result is saturated to - the minimum possible value, on overflow - the maximum possible value. + not deal with Forever. In case of underflow, which is only possible if the + timer has expired, an arbitrary negative value is returned. */ qint64 QDeadlineTimer::rawRemainingTimeNSecs() const noexcept { QDeadlineTimer now = current(timerType()); - TimeReference ref(t1, t2); - - qint64 nsecs; - if (!ref.subtract(now.t1, now.t2)) - return TimeReference::Min; // We can only underflow here - - // If we fail the conversion, t1 < now.t1 means we underflowed, - // thus the deadline had long expired - if (!ref.toNanoseconds(&nsecs)) - return t1 < now.t1 ? TimeReference::Min : TimeReference::Max; - return nsecs; + qint64 r; + if (qSubOverflow(t1, now.t1, &r)) + return -1; // any negative number is fine + return r; } /*! @@ -638,12 +439,11 @@ qint64 QDeadlineTimer::deadline() const noexcept { if (isForever()) return TimeReference::Max; + if (t1 == TimeReference::Min) + return t1; - qint64 result; - if (!TimeReference(t1, t2).toMilliseconds(&result)) - return t1 < 0 ? TimeReference::Min : TimeReference::Max; - - return result; + nanoseconds ns(t1); + return duration_cast<milliseconds>(ns).count(); } /*! @@ -672,11 +472,7 @@ qint64 QDeadlineTimer::deadlineNSecs() const noexcept if (isForever()) return TimeReference::Max; - qint64 result; - if (!TimeReference(t1, t2).toNanoseconds(&result)) - return t1 < 0 ? TimeReference::Min : TimeReference::Max; - - return result; + return t1; } /*! @@ -700,11 +496,7 @@ void QDeadlineTimer::setDeadline(qint64 msecs, Qt::TimerType timerType) noexcept } type = timerType; - - TimeReference ref; - if (!ref.addMilliseconds(msecs)) - ref.saturate(msecs > 0); - ref.updateTimer(t1, t2); + t1 = add_saturate(0, milliseconds{msecs}); } /*! @@ -722,13 +514,7 @@ void QDeadlineTimer::setDeadline(qint64 msecs, Qt::TimerType timerType) noexcept void QDeadlineTimer::setPreciseDeadline(qint64 secs, qint64 nsecs, Qt::TimerType timerType) noexcept { type = timerType; - - // We don't pass the seconds to the constructor, because we don't know - // at this point if t1 holds the seconds or nanoseconds; it's platform specific. - TimeReference ref; - if (!ref.addSecsAndNSecs(secs, nsecs)) - ref.saturate(TimeReference::sign(secs, nsecs)); - ref.updateTimer(t1, t2); + t1 = add_saturate(0, seconds{secs}, nanoseconds{nsecs}); } /*! @@ -744,11 +530,7 @@ QDeadlineTimer QDeadlineTimer::addNSecs(QDeadlineTimer dt, qint64 nsecs) noexcep if (dt.isForever()) return dt; - TimeReference ref(dt.t1, dt.t2); - if (!ref.addNanoseconds(nsecs)) - ref.saturate(nsecs > 0); - ref.updateTimer(dt.t1, dt.t2); - + dt.t1 = add_saturate(dt.t1, nanoseconds{nsecs}); return dt; } @@ -866,11 +648,7 @@ QDeadlineTimer operator+(QDeadlineTimer dt, qint64 msecs) if (dt.isForever()) return dt; - TimeReference ref(dt.t1, dt.t2); - if (!ref.addMilliseconds(msecs)) - ref.saturate(msecs > 0); - ref.updateTimer(dt.t1, dt.t2); - + dt.t1 = add_saturate(dt.t1, milliseconds{msecs}); return dt; } @@ -941,6 +719,4 @@ QDeadlineTimer operator+(QDeadlineTimer dt, qint64 msecs) \internal */ -// the rest of the functions are in qelapsedtimer_xxx.cpp - QT_END_NAMESPACE diff --git a/tests/auto/corelib/kernel/qdeadlinetimer/tst_qdeadlinetimer.cpp b/tests/auto/corelib/kernel/qdeadlinetimer/tst_qdeadlinetimer.cpp index ab8adfc75b..bdf2e7a573 100644 --- a/tests/auto/corelib/kernel/qdeadlinetimer/tst_qdeadlinetimer.cpp +++ b/tests/auto/corelib/kernel/qdeadlinetimer/tst_qdeadlinetimer.cpp @@ -524,7 +524,7 @@ void tst_QDeadlineTimer::overflow() 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()); + QCOMPARE(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); |