Hyprland/src/helpers/time/Time.cpp
Tom Englund 47f9035601
time: ensure type correctness and calculate nsec correctly (#13167)
use auto for nsecdur, assigning system_tp into steady_tp compiles but is
not correct. just change it to auto.

use {} initialization for timespec structs and returning std::pair.

in timediff, fromTimespec and toTimespec the else case was calculating
wrong. we need to correctly handle the borrow when the nanoseconds of
the first time are smaller than the second, by adding TIMESPEC_NSEC_PER_SEC
and decrementing the seconds.
2026-02-01 14:18:06 +00:00

138 lines
4.3 KiB
C++

#include "Time.hpp"
#define chr std::chrono
#define TIMESPEC_NSEC_PER_SEC 1000000000L
using s_ns = std::pair<uint64_t, uint64_t>;
static s_ns timediff(const s_ns& a, const s_ns& b) {
s_ns d;
d.first = a.first - b.first;
if (a.second >= b.second)
d.second = a.second - b.second;
else {
d.second = (TIMESPEC_NSEC_PER_SEC + a.second) - b.second;
d.first -= 1;
}
return d;
}
static s_ns timeadd(const s_ns& a, const s_ns& b) {
s_ns d;
d.first = a.first + b.first;
if (a.second + b.second >= TIMESPEC_NSEC_PER_SEC) {
d.second = a.second + b.second - TIMESPEC_NSEC_PER_SEC;
d.first += 1;
} else
d.second = a.second + b.second;
return d;
}
Time::steady_tp Time::steadyNow() {
return chr::steady_clock::now();
}
Time::system_tp Time::systemNow() {
return chr::system_clock::now();
}
uint64_t Time::millis(const steady_tp& tp) {
return chr::duration_cast<chr::milliseconds>(tp.time_since_epoch()).count();
}
s_ns Time::secNsec(const steady_tp& tp) {
const uint64_t sec = chr::duration_cast<chr::seconds>(tp.time_since_epoch()).count();
const auto nsecdur = tp - chr::steady_clock::time_point(chr::seconds(sec));
return {sec, chr::duration_cast<chr::nanoseconds>(nsecdur).count()};
}
uint64_t Time::millis(const system_tp& tp) {
return chr::duration_cast<chr::milliseconds>(tp.time_since_epoch()).count();
}
s_ns Time::secNsec(const system_tp& tp) {
const uint64_t sec = chr::duration_cast<chr::seconds>(tp.time_since_epoch()).count();
const auto nsecdur = tp - chr::system_clock::time_point(chr::seconds(sec));
return {sec, chr::duration_cast<chr::nanoseconds>(nsecdur).count()};
}
// TODO: this is a mess, but C++ doesn't define what steady_clock is.
// At least on Linux, system_clock == CLOCK_REALTIME
// and steady_clock == CLOCK_MONOTONIC,
// or at least it seems so with gcc and gcc's stl.
// but, since we can't *ever* be sure, we have to guess.
// In general, this may shift the time around by a couple hundred ns. Doesn't matter, realistically.
Time::steady_tp Time::fromTimespec(const timespec* ts) {
timespec mono{}, real{};
clock_gettime(CLOCK_MONOTONIC, &mono);
clock_gettime(CLOCK_REALTIME, &real);
auto now = Time::steadyNow();
auto nowSys = Time::systemNow();
s_ns stdSteady, stdReal;
stdSteady = Time::secNsec(now);
stdReal = Time::secNsec(nowSys);
// timespec difference, REAL - MONO
s_ns diff;
diff.first = real.tv_sec - mono.tv_sec;
if (real.tv_nsec >= mono.tv_nsec)
diff.second = real.tv_nsec - mono.tv_nsec;
else {
diff.second = TIMESPEC_NSEC_PER_SEC + real.tv_nsec - mono.tv_nsec;
diff.first -= 1;
}
// STD difference, REAL - MONO
s_ns diff2 = timediff(stdReal, stdSteady);
s_ns diffFinal;
s_ns monotime = {ts->tv_sec, ts->tv_nsec};
if (diff.first >= diff2.first || (diff.first == diff2.first && diff.second >= diff2.second))
diffFinal = timediff(diff, diff2);
else
diffFinal = timediff(diff2, diff);
auto sum = timeadd(monotime, diffFinal);
return chr::steady_clock::time_point(std::chrono::seconds(sum.first)) + chr::nanoseconds(sum.second);
}
struct timespec Time::toTimespec(const steady_tp& tp) {
struct timespec mono, real;
clock_gettime(CLOCK_MONOTONIC, &mono);
clock_gettime(CLOCK_REALTIME, &real);
Time::steady_tp now = Time::steadyNow();
Time::system_tp nowSys = Time::systemNow();
s_ns stdSteady, stdReal;
stdSteady = Time::secNsec(now);
stdReal = Time::secNsec(nowSys);
// timespec difference, REAL - MONO
s_ns diff;
diff.first = real.tv_sec - mono.tv_sec;
if (real.tv_nsec >= mono.tv_nsec)
diff.second = real.tv_nsec - mono.tv_nsec;
else {
diff.second = TIMESPEC_NSEC_PER_SEC + real.tv_nsec - mono.tv_nsec;
diff.first -= 1;
}
// STD difference, REAL - MONO
s_ns diff2 = timediff(stdReal, stdSteady);
s_ns diffFinal;
s_ns tpTime = secNsec(tp);
if (diff.first >= diff2.first || (diff.first == diff2.first && diff.second >= diff2.second))
diffFinal = timediff(diff, diff2);
else
diffFinal = timediff(diff2, diff);
auto sum = timeadd(tpTime, diffFinal);
return timespec{.tv_sec = sum.first, .tv_nsec = sum.second};
}