Files
TimbreESP32/lib/timbre_core/SessionClock.cpp
Angel Ivan 2a9eabce14 Fist commit
2026-10-02 12:08:21 -06:00

135 lines
4.5 KiB
C++
Executable File

#include "SessionClock.h"
#include <limits>
#include <utility>
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<std::int64_t>(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<std::uint64_t>(predicted - utcEpochSeconds)
: static_cast<std::uint64_t>(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<std::int64_t>(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