diff options
Diffstat (limited to 'src/clock.rs')
| -rw-r--r-- | src/clock.rs | 53 |
1 files changed, 53 insertions, 0 deletions
diff --git a/src/clock.rs b/src/clock.rs new file mode 100644 index 0000000..dafd44b --- /dev/null +++ b/src/clock.rs @@ -0,0 +1,53 @@ +//! Wall-clock time as this node sees it, in Unix milliseconds: commit stamps, alive +//! stamps, remake deadlines and votes, archive and quarantine times. +//! +//! Every such reading goes through the node's [`Clock`], so tests can put one node's +//! clock ahead of or behind the others (`docs/trust.md`, "What depends on clocks"). +//! Elapsed time within a node (timeouts, keepalives) uses tokio's monotonic clock and +//! isn't affected. + +use std::sync::atomic::{AtomicI64, Ordering}; + +/// The node's wall clock: the system clock plus an offset (zero outside tests). +#[derive(Debug, Default)] +pub(crate) struct Clock { + offset_ms: AtomicI64, +} + +impl Clock { + /// Now, in Unix milliseconds. + pub(crate) fn now_millis(&self) -> u64 { + let offset = self.offset_ms.load(Ordering::Relaxed); + system_millis().saturating_add_signed(offset) + } + + /// Puts this clock `offset_ms` ahead of the system clock (behind if negative). + #[cfg(test)] + pub(crate) fn set_offset_ms(&self, offset_ms: i64) { + self.offset_ms.store(offset_ms, Ordering::Relaxed); + } +} + +/// The system clock, in Unix milliseconds; 0 if it is before 1970. +fn system_millis() -> u64 { + std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .map(|d| d.as_millis() as u64) + .unwrap_or(0) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn offset_moves_the_clock() { + let clock = Clock::default(); + let before = clock.now_millis(); + clock.set_offset_ms(3_600_000); + let ahead = clock.now_millis(); + assert!(ahead >= before + 3_600_000); + clock.set_offset_ms(-3_600_000); + assert!(clock.now_millis() < before); + } +} |
