refactor(time): Introduce typed clock sources for compile-time safety

This change replaces runtime `ClockId` parameters with distinct types to ensure correct clock usage at compile time.
This commit is contained in:
Nils Andreas Svee committed 2026-09-30 12:05:42 +07:00
1 parent e27bab1944
commit 817cc304e6
6 files changed
+46 -26

No files matched your search

+2 -3
View File
@@ -3,9 +3,8 @@
// SPDX-License-Identifier: MPL-2.0
use log::{Level, Metadata, Record, SetLoggerError};
use rustix::time::ClockId;
use crate::time::Time;
use crate::time::{ClockSample, Realtime};
#[derive(Clone, Copy)]
pub struct SimpleLogger {
@@ -19,7 +18,7 @@ impl log::Log for SimpleLogger {
fn log(&self, record: &Record) {
if self.enabled(record.metadata()) {
let now = Time::new(ClockId::Realtime);
let now = ClockSample::<Realtime>::now();
println!(
"{} {:5} {}:{}: {}",
now.as_secs_f64(),
+3 -3
View File
@@ -4,7 +4,7 @@
use crate::settings::Settings;
use crate::settings::{MeasurementType, ObservabilityProtocol};
use crate::time::Time;
use crate::time::{ClockSample, Realtime};
use flume::{Receiver, RecvTimeoutError, Sender};
use log::{error, info, warn};
use rustix::time::ClockId;
@@ -148,7 +148,7 @@ impl MetricsSender {
pub fn send(&self, metric: Metric) {
if let Some(ref tx) = self.tx {
let ts = Time::new(ClockId::Realtime).as_nanos();
let ts = ClockSample::<Realtime>::now().as_nanos();
if tx.try_send((metric, ts)).is_err() {
self.dropped.fetch_add(1, Ordering::Relaxed);
}
@@ -197,7 +197,7 @@ impl Metrics {
Err(RecvTimeoutError::Timeout) => {
let dropped = self.metrics_dropped.swap(0, Ordering::Relaxed);
if dropped > 0 {
let ts = Time::new(ClockId::Realtime).as_nanos();
let ts = ClockSample::<Realtime>::now().as_nanos();
batch.push((Metric::Dropped { count: dropped }, ts));
}
if !batch.is_empty() {
+3 -3
View File
@@ -10,7 +10,7 @@ use std::time::Instant;
use crate::pinger::{PingError, PingListener, PingReply, PingSender};
use crate::settings::MeasurementType;
use crate::time::Time;
use crate::time::{ClockSample, Monotonic};
use icmp_socket2::Icmpv4Message;
use icmp_socket2::Icmpv4Packet;
use icmp_socket2::packet::WithEchoRequest;
@@ -54,7 +54,7 @@ impl PingListener for PingerICMPEchoListener {
}
};
let clock = Time::new(ClockId::Monotonic);
let clock = ClockSample::<Monotonic>::now();
let time_ms = clock.to_milliseconds() as i64;
let rtt = (time_ms - time_sent) as f64;
@@ -85,7 +85,7 @@ impl PingListener for PingerICMPEchoListener {
impl PingSender for PingerICMPEchoSender {
fn craft_packet(&self, id: u16, seq: u16) -> (Icmpv4Packet, i64) {
let clock = Time::new(ClockId::Monotonic);
let clock = ClockSample::<Monotonic>::now();
let time_ms = clock.to_milliseconds();
let payload = time_ms.to_be_bytes().to_vec();
+3 -3
View File
@@ -7,7 +7,7 @@
use crate::pinger::{PingError, PingListener, PingReply, PingSender};
use crate::settings::MeasurementType;
use crate::time::Time;
use crate::time::{ClockSample, Realtime};
use icmp_socket2::Icmpv4Message;
use icmp_socket2::Icmpv4Packet;
use icmp_socket2::packet::WithTimestampRequest;
@@ -66,7 +66,7 @@ impl PingListener for PingerICMPTimestampListener {
}
let time_since_midnight =
Time::new(ClockId::Realtime).get_time_since_midnight();
ClockSample::<Realtime>::now().as_time_since_midnight();
let rtt = timestamp_delta(time_since_midnight, originate as i64) as f64;
let dl_time = timestamp_delta(time_since_midnight, transmit as i64) as f64;
@@ -99,7 +99,7 @@ impl PingListener for PingerICMPTimestampListener {
impl PingSender for PingerICMPTimestampSender {
fn craft_packet(&self, id: u16, seq: u16) -> (Icmpv4Packet, i64) {
let time_since_midnight = Time::new(ClockId::Realtime).get_time_since_midnight();
let time_since_midnight = ClockSample::<Realtime>::now().as_time_since_midnight();
(
Icmpv4Packet::with_timestamp_request(id, seq, time_since_midnight as u32, 0, 0)
.unwrap(),
+3 -3
View File
@@ -13,7 +13,7 @@ use crate::platform::{
interface_stats_provider, traffic_control_backend,
};
use crate::settings::Settings;
use crate::time::Time;
use crate::time::{ClockSample, Realtime};
use crate::util::{ArcRwLock, RwLockExt};
use flume::{Receiver, Sender};
use log::{debug, info, warn};
@@ -409,7 +409,7 @@ impl<S: InterfaceStatsProvider, T: TrafficControlBackend> Ratecontroller<S, T> {
self.state_dl.current_rate = self.state_dl.next_rate;
self.state_ul.current_rate = self.state_ul.next_rate;
let stats_time = Time::new(ClockId::Realtime);
let stats_time = ClockSample::<Realtime>::now();
debug!(
"{},{},{},{},{},{},{},{}",
stats_time.secs(),
@@ -459,7 +459,7 @@ impl<S: InterfaceStatsProvider, T: TrafficControlBackend> Ratecontroller<S, T> {
&& now_t.duration_since(lastdump_t).as_secs_f64() > 300.0
{
for i in 0..self.settings.advanced_settings.speed_hist_size as usize {
let hist_time = Time::new(ClockId::Realtime);
let hist_time = ClockSample::<Realtime>::now();
if let Err(e) = fd.write_all(
format!(
"{},{},{},{}\n",
+32 -11
View File
@@ -2,27 +2,42 @@
//
// SPDX-License-Identifier: MPL-2.0
use rustix::fs::Timespec;
use std::marker::PhantomData;
use rustix::time::{ClockId, clock_gettime};
pub struct Time {
time_s: u64,
time_ns: u64,
trait Clock {
const ID: ClockId;
}
impl Time {
pub fn new(id: ClockId) -> Self {
let time: Timespec = clock_gettime(id);
pub enum Realtime {}
pub enum Monotonic {}
impl Clock for Realtime {
const ID: ClockId = ClockId::Realtime;
}
impl Clock for Monotonic {
const ID: ClockId = ClockId::Monotonic;
}
pub struct ClockSample<C> {
time_s: u64,
time_ns: u64,
_clock: PhantomData<C>,
}
impl<C: Clock> ClockSample<C> {
pub fn now() -> Self {
let time = clock_gettime(C::ID);
Self {
time_s: time.tv_sec as u64,
time_ns: time.tv_nsec as u64,
_clock: PhantomData,
}
}
pub fn get_time_since_midnight(&self) -> i64 {
(self.time_s as i64 % 86400 * 1000) + (self.time_ns as i64 / 1000000)
}
pub fn secs(&self) -> u64 {
self.time_s
}
@@ -43,3 +58,9 @@ impl Time {
(self.time_s * 1000) + (self.time_ns / 1000000)
}
}
impl ClockSample<Realtime> {
pub fn as_time_since_midnight(&self) -> i64 {
(self.time_s as i64 % 86_400 * 1000) + self.time_ns as i64 / 1_000_000
}
}