= Module: chronos
:toc: macro
:toclevels: 2
Design notes for the `chronos` module, co-located with its Rust module files. This is the
implementer's view: what the module is for, the two sources it ships, and the
one decision that shapes it (wall time vs a monotonic clock). Product-level
intent lives in xref:../../docs/prd/0003-chronos-time-sources.adoc[PRD 0003]; the
wider plan is in xref:../../docs/target_arch/index.adoc[the target architecture].
toc::[]
== Responsibility
`chronos` is the adapter layer for real, OS-backed physical time. The `kairos`
core owns the `TimeSource` seam (`../kairos/time.rs:7`, re-exported from
`../kairos/mod.rs:13`) and depends only on that trait; `chronos` plugs concrete
sources into it. One reason to change this module: a new concrete `TimeSource` or
a change to how an existing source reads the OS clock.
The whole module is gated behind the `std` feature (`../lib.rs:47`). The `no_std`
core never enables it. `extern crate std;` is declared at the top of `mod.rs:30`,
mirroring how the allocating modules declare `extern crate alloc;`: the std
dependency is localized to the module that needs it.
== Why this module is `std`-only, and why `TickCounter` stays in `kairos`
Both sources read the OS clock (`std::time::SystemTime`, `std::time::Instant`),
which requires `std`. The deterministic `TickCounter` (`../kairos/time.rs:14`)
deliberately does *not* move here: it is `no_std` (an `AtomicU64`), and the
`kairos` property tests depend on it under `cargo test --no-default-features`. A
`std`-gated `chronos` cannot host a source the `no_std` test path needs. So the
split is by capability, not by tidiness: `kairos` keeps the deterministic
`no_std` sources it tests against; `chronos` owns the production `std` sources.
The caller-advanced `VirtualTimeSource` (`../kairos/time.rs:53`) lands in `kairos`
for the same reason: it is just an `Arc<AtomicU64>`, so it is `no_std` and serves
the `--no-default-features` tests. Only a source that reads a real `Instant` (a
deadline source, if one is ever needed) would be `std`-only and belong here; none
ships yet (xref:../../docs/prd/0003-chronos-time-sources.adoc#_open_questions[PRD 0003 open questions]).
== Type inventory
[cols="1,2,3",options="header"]
|===
| Type | Kind | Role
| `SystemTimeSource` | unit struct | Wall-clock `TimeSource`: nanoseconds since
the Unix epoch (`mod.rs:48`; `now` at `mod.rs:63`). The cross-node-comparable
default for a distributed clock.
| `MonotonicTimeSource` | struct (holds a base `Instant`) | Per-process monotonic
`TimeSource`: nanoseconds since construction (`mod.rs:77`; `now` at
`mod.rs:98`). Never steps backward; not comparable across machines.
| `saturating_nanos` | private fn | Saturating `u128`-nanos to `u64` (`mod.rs:38`),
so a reading pins at the `u64` ceiling rather than wrapping. Mirrors the same
helper in `kairos`; duplicated rather than shared to keep `chronos` coupled to
the public `TimeSource` trait only, not a `kairos` private.
|===
== The decision: wall time vs a monotonic clock
This is the one non-obvious choice in the module, so it is stated plainly.
The HLC's `physical` component should track *wall time* across machines, because
that is what makes a `Kairos` comparable between nodes and what lets
`Clock::after(remote)` fold a remote stamp in against a shared scale. So the
recommended production source is `SystemTimeSource` (wall clock), not a monotonic
one, even though the slice that introduced this module was framed as "a monotonic
source."
Wall clocks step backward (NTP corrections, an operator resetting the clock).
That is safe here: `Clock::advance_from` floors the effective physical at the
last minted value and records the regression as backward skew
(`../kairos/clock/mod.rs:258`) rather than correcting it, so the HLC's *output* stays
strictly monotonic no matter how the *input* moves. Absorbing skew is precisely
what the HLC is for.
`MonotonicTimeSource` is the right tool for a different job: single-node ordering,
or measuring elapsed durations (its zero point is process-local, so its readings
are meaningless across a network). It is not part of contention control. S12
resolved PRD 0001 gap 1 by making `Backoff` spin-count based and time-free; a
future real wall-time backoff would be an additive `std`-only `Backoff` strategy
that reads its own clock, not a reason to route contention through this
`TimeSource`.
== Reading semantics and edge cases
`SystemTimeSource::now`:: `SystemTime::now().duration_since(UNIX_EPOCH)`. A clock
set before 1970 makes `duration_since` return `Err`; the source maps that to
`0` (a very old reading) rather than panicking, and the HLC's last-physical
floor carries ordering forward.
`MonotonicTimeSource::now`:: `base.elapsed()` (= `Instant::now() - base`),
monotonic non-decreasing. Two reads within one clock-resolution tick may be
equal; that is non-decreasing, not strictly increasing, which is why the source
test asserts `>=` and the strict-increase guarantee is the HLC's, not the
source's.
Both saturate at ~584 years (the `u64`-nanosecond ceiling) rather than wrapping.
== Test map
Tests live in `src/chronos/tests.rs` and run under any feature set that includes `std`
(`cargo test --features std`, with or without `rand`). The full invariant-to-test mapping is in
xref:../../docs/prd/0003-chronos-time-sources.adoc#_invariants_and_their_tests[PRD 0003].
* `monotonic_source_is_nondecreasing` (`tests.rs:8`): `MonotonicTimeSource` never
steps backward across 1000 reads.
* `system_source_reads_wall_clock` (`tests.rs:23`): `SystemTimeSource` reads true
wall time, bracketed between two independent `SystemTime` samples (one-second
slack for an NTP step); a monotonic-since-boot mistake would fail it.
* `clock_driven_by_system_source_is_strictly_monotonic` (`tests.rs:46`) and
`clock_driven_by_monotonic_source_is_strictly_monotonic` (`tests.rs:68`):
end-to-end, each source drives a real `Clock` whose output is strictly
increasing (and, for the system source, collision-free), proving both are valid
`TimeSource` implementations.
No property tests: the input here is the OS clock, not a generable value, so the
HLC's logic (already covered by the `kairos` proptest harness) is exercised with
example tests instead.