#include "SessionClock.h" #include #include namespace timbre_core { std::uint64_t SessionClock::readMonotonic() const { if (clock_ != nullptr) { return clock_->nowMs(); } if (callback_) { return callback_(); } return 0; } bool SessionClock::setObservation(std::int64_t wallSeconds, std::uint64_t monotonicMs, bool first, bool regression, bool resynchronized) { Observation observation; observation.valid = CivilTime::fromUnixSeconds(wallSeconds, observation.local); observation.firstObservation = first; observation.monotonicRegression = regression; observation.resynchronized = resynchronized; observation.monotonicMs = monotonicMs; observation.utcEpochSeconds = wallSeconds; if (!observation.valid) { lastObservation_ = observation; return false; } initialized_ = true; if (first) { anchorMonotonicMs_ = monotonicMs; anchorWallSeconds_ = wallSeconds; } lastMonotonicMs_ = monotonicMs; lastWallSeconds_ = wallSeconds; lastLocal_ = observation.local; lastObservation_ = observation; return true; } SessionClock::Observation SessionClock::observeAt(std::int64_t utcEpochSeconds, std::uint64_t monotonicMs) { if (!initialized_) { (void)setObservation(utcEpochSeconds, monotonicMs, true, false, false); return lastObservation_; } if (monotonicMs < lastMonotonicMs_) { // A monotonic source must never run backwards. Keep the last good // anchor and reject the sample rather than manufacturing elapsed time. Observation observation = lastObservation_; observation.valid = false; observation.firstObservation = false; observation.monotonicRegression = true; observation.resynchronized = false; observation.monotonicMs = monotonicMs; lastObservation_ = observation; return observation; } const std::uint64_t delta = monotonicMs - lastMonotonicMs_; const std::int64_t predicted = lastWallSeconds_ + static_cast(delta / 1000ULL); // A wall-clock correction is resynchronized at the current sample, never // expanded into a stream of historical minutes. const std::uint64_t difference = predicted >= utcEpochSeconds ? static_cast(predicted - utcEpochSeconds) : static_cast(utcEpochSeconds - predicted); const bool resynchronized = difference > 1ULL; (void)setObservation(utcEpochSeconds, monotonicMs, false, false, resynchronized); return lastObservation_; } SessionClock::Observation SessionClock::observe(std::int64_t utcEpochSeconds) { return observeAt(utcEpochSeconds, readMonotonic()); } SessionClock::Observation SessionClock::tick() { if (!initialized_) { return Observation{}; } const std::uint64_t sample = readMonotonic(); if (sample < lastMonotonicMs_) { Observation observation = lastObservation_; observation.valid = false; observation.firstObservation = false; observation.monotonicRegression = true; observation.resynchronized = false; observation.monotonicMs = sample; lastObservation_ = observation; return observation; } const std::uint64_t delta = sample - lastMonotonicMs_; // Do not use a platform wall clock and do not add a whole missed interval // to a queue. This is the monotonic, bounded session-time path. const std::int64_t wall = lastWallSeconds_ + static_cast(delta / 1000ULL); (void)setObservation(wall, sample, false, false, false); return lastObservation_; } std::uint64_t SessionClock::monotonicNowMs() const { return readMonotonic(); } std::uint64_t SessionClock::elapsedMs() const noexcept { if (!initialized_) { return 0; } const std::uint64_t sample = monotonicNowMs(); return sample < lastMonotonicMs_ ? 0 : sample - lastMonotonicMs_; } std::uint64_t SessionClock::sessionElapsedMs() const noexcept { if (!initialized_) { return 0; } const std::uint64_t sample = monotonicNowMs(); return sample < anchorMonotonicMs_ ? 0 : sample - anchorMonotonicMs_; } void SessionClock::reset() noexcept { initialized_ = false; anchorMonotonicMs_ = 0; lastMonotonicMs_ = 0; anchorWallSeconds_ = 0; lastWallSeconds_ = 0; lastLocal_ = CivilDateTime{}; lastObservation_ = Observation{}; } } // namespace timbre_core