Skip to content

Commit

Permalink
Fix time handling
Browse files Browse the repository at this point in the history
On Linux platforms, where system_clock does not deal in nanoseconds.
  • Loading branch information
erenon committed Feb 6, 2024
1 parent 4cd8f78 commit 3e91749
Showing 1 changed file with 36 additions and 29 deletions.
65 changes: 36 additions & 29 deletions include/binlog/Time.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,36 @@ std::chrono::nanoseconds clockToNsSinceEpoch(const ClockSync& clockSync, std::ui
*/
void nsSinceEpochToBrokenDownTimeUTC(std::chrono::nanoseconds sinceEpoch, BrokenDownTime& dst);

/** Get the duration elapsed since the UNIX epoch in UTC (no leaps seconds) */
inline
#ifdef __linux__
std::chrono::nanoseconds
#else
std::chrono::system_clock::duration
#endif
clockSinceEpoch()
{
// Depending on how libstdc++ is built (_GLIBCXX_USE_CLOCK_GETTIME_SYSCALL),
// system_clock::now() can result in a clock_gettime syscall,
// which is really slow compared to the vsyscall equivalent.
// Call clock_gettime unconditionally on linux:
#ifdef __linux__
struct timespec ts{};
clock_gettime(CLOCK_REALTIME, &ts);
return std::chrono::nanoseconds{
std::chrono::seconds{ts.tv_sec} + std::chrono::nanoseconds{ts.tv_nsec}
};
#else
return std::chrono::system_clock::now().time_since_epoch();
#endif
}

/** Get the number of clock ticks since the UNIX epoch in UTC (no leaps seconds) */
inline std::uint64_t clockNow()
{
return std::uint64_t(clockSinceEpoch().count());
}

/**
* Create a ClockSync corresponding to std::chrono::system_clock.
*
Expand All @@ -67,11 +97,10 @@ void nsSinceEpochToBrokenDownTimeUTC(std::chrono::nanoseconds sinceEpoch, Broken
inline ClockSync systemClockSync()
{
using Clock = std::chrono::system_clock;
static_assert(Clock::period::num == 1, "Clock measures integer fractions of a second");

const auto now = Clock::now();
const auto since_epoch = now.time_since_epoch();
const std::time_t now_tt = Clock::to_time_t(now);
const auto since_epoch = clockSinceEpoch();
const auto now_tp = Clock::time_point(std::chrono::duration_cast<Clock::duration>(since_epoch));
const std::time_t now_tt = Clock::to_time_t(now_tp);

std::tm now_tm{};
#ifdef _WIN32
Expand All @@ -98,12 +127,9 @@ inline ClockSync systemClockSync()
tzName[0] = 0;
}

// on linux, clock_gettime is used directly
#ifdef __linux__
const uint64_t frequency = 1'000'000'000; // nanoseconds
#else
const uint64_t frequency = std::uint64_t(Clock::period::den);
#endif
using Period = decltype(since_epoch)::period;
static_assert(Period::num == 1, "Clock measures integer fractions of a second");
const uint64_t frequency = std::uint64_t(Period::den);

return ClockSync{
std::uint64_t(since_epoch.count()),
Expand All @@ -114,25 +140,6 @@ inline ClockSync systemClockSync()
};
}

/** Get the number of nanoseconds since the UNIX epoch in UTC (no leaps seconds) */
inline std::uint64_t clockNow()
{
// Depending on how libstdc++ is built (_GLIBCXX_USE_CLOCK_GETTIME_SYSCALL),
// system_clock::now() can result in a clock_gettime syscall,
// which is really slow compared to the vsyscall equivalent.
// Call clock_gettime unconditionally on linux:
#ifdef __linux__
struct timespec ts{};
clock_gettime(CLOCK_REALTIME, &ts);
const std::chrono::nanoseconds nanos{
std::chrono::seconds{ts.tv_sec} + std::chrono::nanoseconds{ts.tv_nsec}
};
return std::uint64_t(nanos.count());
#else
return std::uint64_t(std::chrono::system_clock::now().time_since_epoch().count());
#endif
}

} // namespace binlog

#endif // BINLOG_TIME_HPP

0 comments on commit 3e91749

Please sign in to comment.