use crate::source::{EntropySource, Platform, SourceCategory, SourceInfo};
#[cfg(target_os = "macos")]
use crate::sources::helpers::{extract_timing_entropy, mach_time};
static DISPATCH_QUEUE_TIMING_INFO: SourceInfo = SourceInfo {
name: "dispatch_queue_timing",
description: "GCD libdispatch global queue timing — system-wide thread pool entropy",
physics: "Dispatches a no-op block to the BACKGROUND and LOW GCD global queues and \
measures round-trip latency. GCD\u{2019}s global queues are shared across all \
running processes; our measurement reflects the instantaneous load from \
every background task on the system (Spotlight, iCloud, photo analysis, \
app prewarming, etc.). LOW queue CV up to 248%, BACKGROUND H\u{2248}7.3 bits/byte \
on M4 Mac mini. Unlike thread scheduling sources, this captures \
system-wide nondeterminism from a single dispatch call.",
category: SourceCategory::Scheduling,
platform: Platform::MacOS,
requirements: &[],
entropy_rate_estimate: 3.0,
composite: false,
is_fast: false,
};
pub struct DispatchQueueTimingSource;
#[cfg(target_os = "macos")]
mod libdispatch {
use std::ffi::c_void;
pub type DispatchQueueT = *mut c_void;
pub type DispatchSemaphoreT = *mut c_void;
pub const DISPATCH_TIME_NOW: u64 = 0;
pub const _DISPATCH_QUEUE_PRIORITY_HIGH: i64 = 2;
pub const DISPATCH_QUEUE_PRIORITY_LOW: i64 = -2;
pub const DISPATCH_QUEUE_PRIORITY_BACKGROUND: i64 = i16::MIN as i64;
pub const SEMAPHORE_TIMEOUT_NS: i64 = 100_000_000;
#[link(name = "System", kind = "dylib")]
unsafe extern "C" {
pub fn dispatch_get_global_queue(identifier: i64, flags: usize) -> DispatchQueueT;
pub fn dispatch_semaphore_create(value: isize) -> DispatchSemaphoreT;
pub fn dispatch_semaphore_signal(dsema: DispatchSemaphoreT) -> isize;
pub fn dispatch_semaphore_wait(dsema: DispatchSemaphoreT, timeout: u64) -> isize;
pub fn dispatch_time(when: u64, delta: i64) -> u64;
pub fn dispatch_async_f(
queue: DispatchQueueT,
context: *mut c_void,
work: unsafe extern "C" fn(*mut c_void),
);
pub fn dispatch_release(obj: *mut c_void);
}
pub unsafe extern "C" fn signal_semaphore(ctx: *mut std::ffi::c_void) {
let sem = ctx as DispatchSemaphoreT;
unsafe { dispatch_semaphore_signal(sem) };
}
}
#[cfg(target_os = "macos")]
mod imp {
use super::libdispatch::*;
use super::*;
impl EntropySource for DispatchQueueTimingSource {
fn info(&self) -> &SourceInfo {
&DISPATCH_QUEUE_TIMING_INFO
}
fn is_available(&self) -> bool {
true
}
fn collect(&self, n_samples: usize) -> Vec<u8> {
let raw_count = n_samples * 8 + 64;
let mut timings = Vec::with_capacity(raw_count);
let priorities = [
DISPATCH_QUEUE_PRIORITY_LOW,
DISPATCH_QUEUE_PRIORITY_BACKGROUND,
];
for i in 0..32_usize {
let queue =
unsafe { dispatch_get_global_queue(priorities[i % priorities.len()], 0) };
let sem = unsafe { dispatch_semaphore_create(0) };
if sem.is_null() {
continue;
}
unsafe {
let timeout = dispatch_time(DISPATCH_TIME_NOW, SEMAPHORE_TIMEOUT_NS);
dispatch_async_f(queue, sem, signal_semaphore);
dispatch_semaphore_wait(sem, timeout);
dispatch_release(sem);
}
}
for i in 0..raw_count {
let queue =
unsafe { dispatch_get_global_queue(priorities[i % priorities.len()], 0) };
let sem = unsafe { dispatch_semaphore_create(0) };
if sem.is_null() {
continue;
}
let t0 = mach_time();
let timed_out = unsafe {
let timeout = dispatch_time(DISPATCH_TIME_NOW, SEMAPHORE_TIMEOUT_NS);
dispatch_async_f(queue, sem, signal_semaphore);
dispatch_semaphore_wait(sem, timeout) != 0
};
let elapsed = mach_time().wrapping_sub(t0);
unsafe { dispatch_release(sem) };
if !timed_out && elapsed < 240_000 {
timings.push(elapsed);
}
}
extract_timing_entropy(&timings, n_samples)
}
}
}
#[cfg(not(target_os = "macos"))]
impl EntropySource for DispatchQueueTimingSource {
fn info(&self) -> &SourceInfo {
&DISPATCH_QUEUE_TIMING_INFO
}
fn is_available(&self) -> bool {
false
}
fn collect(&self, _n_samples: usize) -> Vec<u8> {
Vec::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn info() {
let src = DispatchQueueTimingSource;
assert_eq!(src.info().name, "dispatch_queue_timing");
assert!(matches!(src.info().category, SourceCategory::Scheduling));
assert_eq!(src.info().platform, Platform::MacOS);
assert!(!src.info().composite);
}
#[test]
#[cfg(target_os = "macos")]
fn is_available_on_macos() {
assert!(DispatchQueueTimingSource.is_available());
}
#[test]
#[ignore] fn collects_bytes_with_variation() {
let src = DispatchQueueTimingSource;
if !src.is_available() {
return;
}
let data = src.collect(32);
assert!(!data.is_empty());
let unique: std::collections::HashSet<u8> = data.iter().copied().collect();
assert!(unique.len() > 4, "expected variation from GCD pool jitter");
}
}