135 lines
4.5 KiB
C++
Executable File
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
|