summaryrefslogtreecommitdiffstats
path: root/src/clock.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/clock.rs')
-rw-r--r--src/clock.rs53
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);
+ }
+}