openentropy_core/sources/timing/
mach_continuous_timing.rs1use crate::source::{EntropySource, Platform, SourceCategory, SourceInfo};
48
49#[cfg(target_os = "macos")]
50use crate::sources::helpers::{extract_timing_entropy, mach_time};
51
52static MACH_CONTINUOUS_TIMING_INFO: SourceInfo = SourceInfo {
53 name: "mach_continuous_timing",
54 description: "mach_continuous_time() kernel sleep-offset path — CV=475% vs abs_time 106%",
55 physics: "Times mach_continuous_time() calls, which unlike mach_absolute_time() must \
56 read the accumulated sleep offset from a kernel structure (seqlock-protected, \
57 updated on every sleep/wake cycle). This creates 4.5× higher CV than \
58 mach_absolute_time(): mean=20.26 ticks, CV=474.9%, range=[0,5166]. \
59 Captures: kernel sleep-offset structure lock contention, cache pressure from \
60 power management kernel thread, residuals from recent sleep/wake cycles. \
61 The maximum (5166 ticks = 215µs) vs typical (0 ticks, same tick) creates \
62 a sparse-event distribution similar to CNTPCT physical timer.",
63 category: SourceCategory::Timing,
64 platform: Platform::MacOS,
65 requirements: &[],
66 entropy_rate_estimate: 2.0,
67 composite: false,
68 is_fast: false,
69};
70
71pub struct MachContinuousTimingSource;
73
74#[cfg(target_os = "macos")]
75unsafe extern "C" {
76 fn mach_continuous_time() -> u64;
77}
78
79#[cfg(target_os = "macos")]
80impl EntropySource for MachContinuousTimingSource {
81 fn info(&self) -> &SourceInfo {
82 &MACH_CONTINUOUS_TIMING_INFO
83 }
84
85 fn is_available(&self) -> bool {
86 true
87 }
88
89 fn collect(&self, n_samples: usize) -> Vec<u8> {
90 let raw = n_samples * 4 + 64;
91 let mut timings = Vec::with_capacity(raw);
92
93 for _ in 0..16 {
95 unsafe { mach_continuous_time() };
96 }
97
98 for _ in 0..raw {
99 let t0 = mach_time();
100 let _ct = unsafe { mach_continuous_time() };
101 let elapsed = mach_time().wrapping_sub(t0);
102
103 if elapsed < 240_000 {
105 timings.push(elapsed);
106 }
107 }
108
109 extract_timing_entropy(&timings, n_samples)
110 }
111}
112
113#[cfg(not(target_os = "macos"))]
114impl EntropySource for MachContinuousTimingSource {
115 fn info(&self) -> &SourceInfo {
116 &MACH_CONTINUOUS_TIMING_INFO
117 }
118 fn is_available(&self) -> bool {
119 false
120 }
121 fn collect(&self, _: usize) -> Vec<u8> {
122 Vec::new()
123 }
124}
125
126#[cfg(test)]
127mod tests {
128 use super::*;
129
130 #[test]
131 fn info() {
132 let src = MachContinuousTimingSource;
133 assert_eq!(src.info().name, "mach_continuous_timing");
134 assert!(matches!(src.info().category, SourceCategory::Timing));
135 assert_eq!(src.info().platform, Platform::MacOS);
136 }
137
138 #[test]
139 #[cfg(target_os = "macos")]
140 fn is_available_on_macos() {
141 assert!(MachContinuousTimingSource.is_available());
142 }
143
144 #[test]
145 #[ignore]
146 fn collects_sleep_offset_timing() {
147 let data = MachContinuousTimingSource.collect(32);
148 assert!(!data.is_empty());
149 }
150}