Skip to content

Commit

Permalink
QElapsedTimer: rewrite using std::chrono::steady_clock everywhere
Browse files Browse the repository at this point in the history
This commit deletes the direct, low-level functionality that
QElapsedTimer has carried since it was introduced. Everything now uses
only std::chrono::steady_clock and std::chrono::nanoseconds.

QDeadlineTimer temporarily still uses qt_gettime(), which is moved to
qcore_unix.cpp.

Task-number: QTBUG-110059
Change-Id: Ieec322d73c1e40ad95c8fffd174641a469b1eee5
Reviewed-by: Ahmad Samir <a.samirh78@gmail.com>
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
  • Loading branch information
thiagomacieira committed Apr 18, 2023
1 parent 13f0ee0 commit c9f4c0d
Show file tree
Hide file tree
Showing 11 changed files with 297 additions and 464 deletions.
6 changes: 0 additions & 6 deletions src/corelib/CMakeLists.txt
Expand Up @@ -512,7 +512,6 @@ qt_internal_extend_target(Core CONDITION WIN32
io/qwindowspipewriter.cpp io/qwindowspipewriter_p.h
io/qntdll_p.h
kernel/qcoreapplication_win.cpp
kernel/qelapsedtimer_win.cpp
kernel/qeventdispatcher_win.cpp kernel/qeventdispatcher_win_p.h
kernel/qfunctions_win.cpp kernel/qfunctions_win_p.h kernel/qfunctions_winrt_p.h
ipc/qsharedmemory_win.cpp
Expand Down Expand Up @@ -972,11 +971,6 @@ qt_internal_extend_target(Core CONDITION APPLE AND NOT MACOS
${FWMobileCoreServices}
)

qt_internal_extend_target(Core CONDITION UNIX
SOURCES
kernel/qelapsedtimer_unix.cpp
)

qt_internal_extend_target(Core CONDITION ANDROID
SOURCES
io/qstandardpaths_android.cpp
Expand Down
28 changes: 28 additions & 0 deletions src/corelib/kernel/qcore_unix.cpp
Expand Up @@ -65,6 +65,34 @@ int qt_open64(const char *pathname, int flags, mode_t mode)

#ifndef QT_BOOTSTRAPPED

static inline void do_gettime(qint64 *sec, qint64 *frac)
{
timespec ts;
clockid_t clk = CLOCK_REALTIME;
#if defined(CLOCK_MONOTONIC_RAW)
clk = CLOCK_MONOTONIC_RAW;
#elif defined(CLOCK_MONOTONIC)
clk = CLOCK_MONOTONIC;
#endif

clock_gettime(clk, &ts);
*sec = ts.tv_sec;
*frac = ts.tv_nsec;
}

// also used in qeventdispatcher_unix.cpp
struct timespec qt_gettime() noexcept
{
qint64 sec, frac;
do_gettime(&sec, &frac);

timespec tv;
tv.tv_sec = sec;
tv.tv_nsec = frac;

return tv;
}

#if QT_CONFIG(poll_pollts)
# define ppoll pollts
#endif
Expand Down
21 changes: 21 additions & 0 deletions src/corelib/kernel/qdeadlinetimer.cpp
Expand Up @@ -5,6 +5,10 @@
#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)
Expand Down Expand Up @@ -836,6 +840,23 @@ QDeadlineTimer QDeadlineTimer::addNSecs(QDeadlineTimer dt, qint64 nsecs) noexcep
The QDeadlineTimer object will be constructed with the specified \a timerType.
*/
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();
result.t1 = ns.count();
#endif
result.type = timerType;
return result;
}

/*!
\fn bool QDeadlineTimer::operator==(QDeadlineTimer d1, QDeadlineTimer d2)
Expand Down
215 changes: 215 additions & 0 deletions src/corelib/kernel/qelapsedtimer.cpp
Expand Up @@ -165,9 +165,219 @@ QT_BEGIN_NAMESPACE
function will return false.
*/

/*!
\fn QElapsedTimer::clockType() noexcept
Returns the clock type that this QElapsedTimer implementation uses.
Since Qt 6.6, QElapsedTimer uses \c{std::chrono::steady_clock}, so the
clock type is always \l MonotonicClock.
\sa isMonotonic()
*/

QElapsedTimer::ClockType QElapsedTimer::clockType() noexcept
{
// we use std::chrono::steady_clock
return MonotonicClock;
}

/*!
\fn QElapsedTimer::isMonotonic() noexcept
Returns \c true if this is a monotonic clock, false otherwise. See the
information on the different clock types to understand which ones are
monotonic.
Since Qt 6.6, QElapsedTimer uses \c{std::chrono::steady_clock}, so this
function now always returns true.
\sa clockType(), QElapsedTimer::ClockType
*/
bool QElapsedTimer::isMonotonic() noexcept
{
// We trust std::chrono::steady_clock to be steady (monotonic); if the
// Standard Library is lying to us, users must complain to their vendor.
return true;
}

/*!
Starts this timer. Once started, a timer value can be checked with elapsed() or msecsSinceReference().
Normally, a timer is started just before a lengthy operation, such as:
\snippet qelapsedtimer/main.cpp 0
Also, starting a timer makes it valid again.
\sa restart(), invalidate(), elapsed()
*/
void QElapsedTimer::start() noexcept
{
static_assert(sizeof(t1) == sizeof(Duration::rep));

// This assignment will work so long as TimePoint uses the same time
// duration or one of finer granularity than steady_clock::time_point. That
// means it will work until the first steady_clock using picoseconds.
TimePoint now = std::chrono::steady_clock::now();
t1 = now.time_since_epoch().count();
QT6_ONLY(t2 = 0);
}

/*!
Restarts the timer and returns the number of milliseconds elapsed since
the previous start.
This function is equivalent to obtaining the elapsed time with elapsed()
and then starting the timer again with start(), but it does so in one
single operation, avoiding the need to obtain the clock value twice.
Calling this function on a QElapsedTimer that is invalid
results in undefined behavior.
The following example illustrates how to use this function to calibrate a
parameter to a slow operation (for example, an iteration count) so that
this operation takes at least 250 milliseconds:
\snippet qelapsedtimer/main.cpp 3
\sa start(), invalidate(), elapsed(), isValid()
*/
qint64 QElapsedTimer::restart() noexcept
{
QElapsedTimer old = *this;
start();
return old.msecsTo(*this);
}

/*!
\since 6.6
Returns a \c{std::chrono::nanoseconds} with the time since this QElapsedTimer was last
started.
Calling this function on a QElapsedTimer that is invalid
results in undefined behavior.
On platforms that do not provide nanosecond resolution, the value returned
will be the best estimate available.
\sa start(), restart(), hasExpired(), invalidate()
*/
auto QElapsedTimer::durationElapsed() const noexcept -> Duration
{
TimePoint then{Duration(t1)};
return std::chrono::steady_clock::now() - then;
}

/*!
\since 4.8
Returns the number of nanoseconds since this QElapsedTimer was last
started.
Calling this function on a QElapsedTimer that is invalid
results in undefined behavior.
On platforms that do not provide nanosecond resolution, the value returned
will be the best estimate available.
\sa start(), restart(), hasExpired(), invalidate()
*/
qint64 QElapsedTimer::nsecsElapsed() const noexcept
{
return durationElapsed().count();
}

/*!
Returns the number of milliseconds since this QElapsedTimer was last
started.
Calling this function on a QElapsedTimer that is invalid
results in undefined behavior.
\sa start(), restart(), hasExpired(), isValid(), invalidate()
*/
qint64 QElapsedTimer::elapsed() const noexcept
{
using namespace std::chrono;
return duration_cast<milliseconds>(durationElapsed()).count();
}

/*!
Returns the number of milliseconds between last time this QElapsedTimer
object was started and its reference clock's start.
This number is usually arbitrary for all clocks except the
QElapsedTimer::SystemTime clock. For that clock type, this number is the
number of milliseconds since January 1st, 1970 at 0:00 UTC (that is, it
is the Unix time expressed in milliseconds).
On Linux, Windows and Apple platforms, this value is usually the time
since the system boot, though it usually does not include the time the
system has spent in sleep states.
\sa clockType(), elapsed()
*/
qint64 QElapsedTimer::msecsSinceReference() const noexcept
{
using namespace std::chrono;
return duration_cast<milliseconds>(Duration(t1)).count();
}

/*!
\since 6.6
Returns the time difference between this QElapsedTimer and \a other as a
\c{std::chrono::nanoseconds}. If \a other was started before this object,
the returned value will be negative. If it was started later, the returned
value will be positive.
The return value is undefined if this object or \a other were invalidated.
\sa secsTo(), elapsed()
*/
auto QElapsedTimer::durationTo(const QElapsedTimer &other) const noexcept -> Duration
{
Duration d1(t1);
Duration d2(other.t1);
return d2 - d1;
}

/*!
Returns the number of milliseconds between this QElapsedTimer and \a
other. If \a other was started before this object, the returned value
will be negative. If it was started later, the returned value will be
positive.
The return value is undefined if this object or \a other were invalidated.
\sa secsTo(), elapsed()
*/
qint64 QElapsedTimer::msecsTo(const QElapsedTimer &other) const noexcept
{
using namespace std::chrono;
return duration_cast<milliseconds>(durationTo(other)).count();
}

/*!
Returns the number of seconds between this QElapsedTimer and \a other. If
\a other was started before this object, the returned value will be
negative. If it was started later, the returned value will be positive.
Calling this function on or with a QElapsedTimer that is invalid
results in undefined behavior.
\sa msecsTo(), elapsed()
*/
qint64 QElapsedTimer::secsTo(const QElapsedTimer &other) const noexcept
{
using namespace std::chrono;
return duration_cast<seconds>(durationTo(other)).count();
}

static const qint64 invalidData = Q_INT64_C(0x8000000000000000);

/*!
\fn QElapsedTimer::invalidate() noexcept
Marks this QElapsedTimer object as invalid.
An invalid object can be checked with isValid(). Calculations of timer
Expand Down Expand Up @@ -207,4 +417,9 @@ bool QElapsedTimer::hasExpired(qint64 timeout) const noexcept
return quint64(elapsed()) > quint64(timeout);
}

bool operator<(const QElapsedTimer &lhs, const QElapsedTimer &rhs) noexcept
{
return lhs.t1 < rhs.t1;
}

QT_END_NAMESPACE
9 changes: 8 additions & 1 deletion src/corelib/kernel/qelapsedtimer.h
Expand Up @@ -6,8 +6,9 @@

#include <QtCore/qglobal.h>

QT_BEGIN_NAMESPACE
#include <chrono>

QT_BEGIN_NAMESPACE

class Q_CORE_EXPORT QElapsedTimer
{
Expand All @@ -21,6 +22,10 @@ class Q_CORE_EXPORT QElapsedTimer
PerformanceCounter
};

// similar to std::chrono::*_clock
using Duration = std::chrono::nanoseconds;
using TimePoint = std::chrono::time_point<std::chrono::steady_clock, Duration>;

constexpr QElapsedTimer() = default;

static ClockType clockType() noexcept;
Expand All @@ -31,11 +36,13 @@ class Q_CORE_EXPORT QElapsedTimer
void invalidate() noexcept;
bool isValid() const noexcept;

Duration durationElapsed() const noexcept;
qint64 nsecsElapsed() const noexcept;
qint64 elapsed() const noexcept;
bool hasExpired(qint64 timeout) const noexcept;

qint64 msecsSinceReference() const noexcept;
Duration durationTo(const QElapsedTimer &other) const noexcept;
qint64 msecsTo(const QElapsedTimer &other) const noexcept;
qint64 secsTo(const QElapsedTimer &other) const noexcept;

Expand Down

0 comments on commit c9f4c0d

Please sign in to comment.