Skip to main content

openentropy_core/sources/scheduling/
sleep_jitter.rs

1//! Sleep jitter entropy source.
2//!
3//! Requests zero-duration sleeps and measures the actual elapsed time to
4//! capture OS scheduler non-determinism.
5
6use std::thread;
7use std::time::{Duration, Instant};
8
9use crate::source::{EntropySource, Platform, SourceCategory, SourceInfo};
10use crate::sources::helpers::extract_timing_entropy;
11
12/// Requests zero-duration sleeps and measures the actual elapsed time.
13/// The jitter captures OS scheduler non-determinism: timer interrupt
14/// granularity, thread priority decisions, runqueue length, and DVFS.
15pub 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] // Run with: cargo test -- --ignored
62    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}