Periodic recalibration (see docs/explanation/architecture.md, "Clock calibration"): a calibrator thread measures with calibrate_tsc() and publishes into a Seqlocked<TscCalibration>; every TscClock user keeps its own copy and calls refresh() from its loop.
More...
#include <fastmm/core/time.hpp>
Periodic recalibration (see docs/explanation/architecture.md, "Clock calibration"): a calibrator thread measures with calibrate_tsc() and publishes into a Seqlocked<TscCalibration>; every TscClock user keeps its own copy and calls refresh() from its loop.
A clock is owned by one thread: refresh() and the time accessors must not race.
◆ TscClock() [1/2]
| fastmm::TscClock::TscClock |
( |
| ) |
|
|
defaultnoexcept |
◆ TscClock() [2/2]
◆ calibrate()
Blocking; call once at startup, before the clock is shared with another thread.
◆ set_calibration()
◆ calibration()
◆ attach_calibration_source()
Subscribes to published calibrations.
The next refresh() takes whatever is published (even the version that was current at attach time); after that only new versions. slew_horizon should match the calibrator's period: each re-anchor absorbs the measured offset over that horizon (see reanchor()); zero disables slewing.
◆ calibration_source()
| const Seqlocked< TscCalibration > * fastmm::TscClock::calibration_source |
( |
| ) |
const |
|
inlinenoexcept |
◆ refresh()
| FASTMM_FORCE_INLINE TscRefresh fastmm::TscClock::refresh |
( |
| ) |
|
|
inlinenoexcept |
Cheap when nothing changed (one atomic load); copies and re-anchors on a new version.
A write in progress is skipped and picked up by a later call.
◆ reanchor()
Switches to fresh at TSC reading at (refresh() passes rdtsc()).
If both mappings use the TSC and agree at at within the step threshold, the clock re-anchors at the old mapping's time for at, so time never jumps, and runs at the fresh rate plus a bounded correction that absorbs the measured offset over the slew horizon: offset * rate / horizon, so after horizon ns the mapping agrees with fresh (clamped to kMaxSlewPpm). Without the correction the offset would accumulate across re-anchors until it forced a step. If the offset exceeds the threshold the clock steps to fresh instead.
◆ steps()
| std::uint64_t fastmm::TscClock::steps |
( |
| ) |
const |
|
inlinenoexcept |
Diagnostics (owner thread, or after the owner stopped).
◆ reanchors()
| std::uint64_t fastmm::TscClock::reanchors |
( |
| ) |
const |
|
inlinenoexcept |
◆ last_offset_ns()
| std::int64_t fastmm::TscClock::last_offset_ns |
( |
| ) |
const |
|
inlinenoexcept |
fresh - old at the last re-anchor or step, in ns.
◆ step_threshold()
| Duration fastmm::TscClock::step_threshold |
( |
| ) |
const |
|
inlinenoexcept |
◆ slew_horizon()
| Duration fastmm::TscClock::slew_horizon |
( |
| ) |
const |
|
inlinenoexcept |
◆ slew_ppb()
| std::int64_t fastmm::TscClock::slew_ppb |
( |
| ) |
const |
|
inlinenoexcept |
Rate correction applied at the last re-anchor, in parts per billion of the fresh rate.
◆ now()
| FASTMM_FORCE_INLINE Timestamp fastmm::TscClock::now |
( |
| ) |
const |
|
inlinenoexcept |
◆ cycles()
| FASTMM_FORCE_INLINE Cycles fastmm::TscClock::cycles |
( |
| ) |
const |
|
inlinenoexcept |
TSC cycles whenever the rate is known (also without an invariant TSC), so that they share a time base with the network threads' rdtscp stamps; wall ns without a rate.
◆ to_timestamp()
| FASTMM_FORCE_INLINE Timestamp fastmm::TscClock::to_timestamp |
( |
Cycles |
c | ) |
const |
|
inlinenoexcept |
◆ cycles_to_ns()
| FASTMM_FORCE_INLINE std::int64_t fastmm::TscClock::cycles_to_ns |
( |
std::uint64_t |
dc | ) |
const |
|
inlinenoexcept |
◆ kDefaultStepThreshold
◆ kDefaultSlewHorizon
◆ kMaxSlewPpm
| constexpr std::int64_t fastmm::TscClock::kMaxSlewPpm = 500 |
|
staticconstexpr |
Upper bound on the rate adjustment used to absorb an offset (a clock servo, not a jump).