openentropy_core/sources/scheduling/
sleep_jitter.rs1use std::thread;
7use std::time::{Duration, Instant};
8
9use crate::source::{EntropySource, Platform, SourceCategory, SourceInfo};
10use crate::sources::helpers::extract_timing_entropy;
11
12pub struct SleepJitterSource;
16
17static SLEEP_JITTER_INFO: SourceInfo = SourceInfo {
18 name: "sleep_jitter",
19 description: "OS scheduler jitter from zero-duration sleeps",
20 physics: "Requests zero-duration sleeps and measures actual wake time. The jitter \
21 captures OS scheduler non-determinism: timer interrupt granularity (1-4ms), \
22 thread priority decisions, runqueue length, and thermal-dependent clock \
23 frequency scaling (DVFS).",
24 category: SourceCategory::Scheduling,
25 platform: Platform::Any,
26 requirements: &[],
27 entropy_rate_estimate: 0.4,
28 composite: false,
29 is_fast: true,
30};
31
32impl EntropySource for SleepJitterSource {
33 fn info(&self) -> &SourceInfo {
34 &SLEEP_JITTER_INFO
35 }
36
37 fn is_available(&self) -> bool {
38 true
39 }
40
41 fn collect(&self, n_samples: usize) -> Vec<u8> {
42 let oversample = n_samples * 2 + 64;
43 let mut raw_timings = Vec::with_capacity(oversample);
44
45 for _ in 0..oversample {
46 let before = Instant::now();
47 thread::sleep(Duration::ZERO);
48 let elapsed_ns = before.elapsed().as_nanos() as u64;
49 raw_timings.push(elapsed_ns);
50 }
51
52 extract_timing_entropy(&raw_timings, n_samples)
53 }
54}
55
56#[cfg(test)]
57mod tests {
58 use super::*;
59
60 #[test]
61 #[ignore] fn sleep_jitter_collects_bytes() {
63 let src = SleepJitterSource;
64 assert!(src.is_available());
65 let data = src.collect(64);
66 assert!(!data.is_empty());
67 assert!(data.len() <= 64);
68 }
69
70 #[test]
71 fn source_info_name() {
72 assert_eq!(SleepJitterSource.name(), "sleep_jitter");
73 }
74
75 #[test]
76 fn source_info_category() {
77 assert_eq!(
78 SleepJitterSource.info().category,
79 SourceCategory::Scheduling
80 );
81 assert!(!SleepJitterSource.info().composite);
82 }
83}