Skip to main content

protocol_droid/application/session/
clock.rs

1//! Where the session's sense of time comes from.
2//!
3//! # Why a port and not `Instant::now`
4//!
5//! The session engine decides that a request has outlived its budget, and that
6//! decision is arithmetic over two readings. If it took the second reading from
7//! a global clock, a test could not set up a request that started 90 seconds
8//! ago without sleeping for 90 seconds, and a deterministic domain would stop
9//! being one. Everything else follows from this: the clock is injected, the
10//! reading is a [`std::time::Duration`] from an origin the adapter chose, and no session
11//! code calls a clock function at all.
12//!
13//! # Why the origin is the adapter's business
14//!
15//! Two sessions opened in the same millisecond must still be distinguishable,
16//! and a wall clock can step backwards under NTP. A duration from a monotonic
17//! source has neither problem, and which monotonic source is the transport's
18//! problem — `Instant` against process start, a monotonic clock value, a
19//! virtual time in a test.
20
21use crate::domain::session::Instant;
22
23/// A monotonic source of time.
24///
25/// Declared by the engine, which is its only consumer, and consumed through
26/// `impl Clock` so the call monomorphises. One method: the engine reads the
27/// clock and does the arithmetic itself, because "how long is a sampling
28/// request allowed to take" is a session decision and a timer that knows the
29/// policy would be a policy in an adapter.
30///
31/// # Examples
32///
33/// ```
34/// use std::time::Duration;
35/// use protocol_droid::application::session::Clock;
36/// use protocol_droid::domain::session::Instant;
37///
38/// /// A clock the test drives by hand.
39/// struct TestClock(std::sync::atomic::AtomicU64);
40///
41/// impl Clock for TestClock {
42///     fn now(&self) -> Instant {
43///         let millis = self.0.load(std::sync::atomic::Ordering::Relaxed);
44///         Instant::from_duration(Duration::from_millis(millis))
45///     }
46/// }
47///
48/// let clock = TestClock(std::sync::atomic::AtomicU64::new(1_000));
49/// assert_eq!(clock.now().as_duration(), Duration::from_secs(1));
50/// ```
51pub trait Clock {
52    /// The current reading.
53    ///
54    /// Only the difference between two readings of the same clock is
55    /// meaningful; the origin carries no information.
56    fn now(&self) -> Instant;
57}