use std::time::Duration;
pub(super) const FREEZE_RENDEZVOUS_TIMEOUT: Duration = Duration::from_secs(30);
#[derive(Debug, Clone, Copy)]
pub(super) enum BspExitReason {
ExternalKill,
Shutdown,
Fatal,
RunError,
GuestPanic,
}
pub(super) struct SnapshotRequest {
pub(super) request_id: u32,
pub(super) kind: u32,
pub(super) tag: String,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(super) enum FreezeState {
Idle,
TookEarly,
Done,
}
pub(super) fn periodic_tag(idx: u32) -> String {
format!("periodic_{:03}", idx)
}
pub(super) fn compute_periodic_boundaries_ns(
window_start_ns: u64,
window_end_ns: u64,
num_snapshots: u32,
) -> Vec<u64> {
if num_snapshots == 0 || window_end_ns <= window_start_ns {
return Vec::new();
}
let n = num_snapshots as u128;
let avail = (window_end_ns - window_start_ns) as u128;
let pre_buffer = avail / 10;
let window = avail.saturating_sub(2u128.saturating_mul(pre_buffer));
if window < n + 1 {
return Vec::new();
}
let mut boundaries: Vec<u64> = Vec::with_capacity(num_snapshots as usize);
for i in 0..n {
let offset = pre_buffer.saturating_add(window.saturating_mul(i + 1) / (n + 1));
let absolute = (window_start_ns as u128).saturating_add(offset);
boundaries.push(u64::try_from(absolute).unwrap_or(u64::MAX));
}
boundaries
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(super) struct PeriodicWindow {
pub(super) window_start_ns: u64,
pub(super) window_end_ns: u64,
pub(super) anchor_ns: u64,
}
pub(super) fn resolve_periodic_window(
scenario_anchor: u64,
periodic_prereqs_ready_ns: u64,
workload_duration_ns: u64,
) -> Option<PeriodicWindow> {
if scenario_anchor == 0 {
return None;
}
Some(PeriodicWindow {
window_start_ns: scenario_anchor.max(periodic_prereqs_ready_ns),
window_end_ns: scenario_anchor.saturating_add(workload_duration_ns),
anchor_ns: scenario_anchor,
})
}
pub(super) fn periodic_accessor_current(last_sched_kva: u64, live_sched_kva: u64) -> bool {
live_sched_kva != 0 && live_sched_kva == last_sched_kva
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn periodic_accessor_current_when_root_unchanged() {
assert!(periodic_accessor_current(
0xffff_8000_1234_5678,
0xffff_8000_1234_5678
));
}
#[test]
fn periodic_accessor_stale_after_swap() {
assert!(!periodic_accessor_current(
0xffff_8000_1111_1111,
0xffff_8000_2222_2222
));
}
#[test]
fn periodic_accessor_detached_or_unresolved_defers() {
assert!(!periodic_accessor_current(0xffff_8000_1111_1111, 0));
assert!(!periodic_accessor_current(0, 0));
assert!(!periodic_accessor_current(0, 0xffff_8000_2222_2222));
}
#[test]
fn periodic_tag_name_format_low_index() {
assert_eq!(periodic_tag(0), "periodic_000");
assert_eq!(periodic_tag(1), "periodic_001");
assert_eq!(periodic_tag(7), "periodic_007");
}
#[test]
fn periodic_tag_name_format_high_index() {
assert_eq!(periodic_tag(10), "periodic_010");
assert_eq!(periodic_tag(63), "periodic_063");
assert_eq!(periodic_tag(64), "periodic_064");
}
#[test]
fn compute_periodic_boundaries_ns_n1_lands_at_midpoint() {
let boundaries = compute_periodic_boundaries_ns(0, 10_000_000_000, 1);
assert_eq!(
boundaries.len(),
1,
"N=1 must produce exactly one interior boundary",
);
assert_eq!(
boundaries[0], 5_000_000_000,
"N=1 lands at window_start + 0.5·(window_end - window_start) = 5 s",
);
}
#[test]
fn compute_periodic_boundaries_ns_n3_quartile_landings() {
let boundaries = compute_periodic_boundaries_ns(0, 10_000_000_000, 3);
assert_eq!(boundaries.len(), 3, "N=3 must produce 3 boundaries");
assert_eq!(boundaries[0], 3_000_000_000, "N=3 boundary[0] at 0.3·d");
assert_eq!(boundaries[1], 5_000_000_000, "N=3 boundary[1] at 0.5·d");
assert_eq!(boundaries[2], 7_000_000_000, "N=3 boundary[2] at 0.7·d");
}
#[test]
fn compute_periodic_boundaries_ns_respects_window_start_offset() {
let window_start_ns: u64 = 2_000_000_000;
let window_end_ns: u64 = 12_000_000_000;
let boundaries = compute_periodic_boundaries_ns(window_start_ns, window_end_ns, 1);
assert_eq!(boundaries.len(), 1);
assert_eq!(
boundaries[0], 7_000_000_000,
"window_start(2 s) + midpoint(5 s of the 10 s span) = 7 s",
);
}
#[test]
fn compute_periodic_boundaries_ns_n0_yields_empty() {
let boundaries = compute_periodic_boundaries_ns(0, 10_000_000_000, 0);
assert!(
boundaries.is_empty(),
"N=0 must yield no boundaries, got {boundaries:?}",
);
}
#[test]
fn compute_periodic_boundaries_ns_strictly_monotonic() {
for &n in &[1u32, 2, 3, 7, 16, 32, 64] {
let boundaries = compute_periodic_boundaries_ns(0, 60_000_000_000, n);
assert_eq!(
boundaries.len(),
n as usize,
"N={n} must produce exactly {n} boundaries",
);
for w in boundaries.windows(2) {
assert!(
w[0] < w[1],
"boundaries must be strictly monotonic; got {w:?} for N={n}",
);
}
}
}
#[test]
fn compute_periodic_boundaries_ns_within_buffer_window() {
let total_ns: u64 = 10_000_000_000;
for &n in &[1u32, 4, 8] {
let boundaries = compute_periodic_boundaries_ns(0, total_ns, n);
for (i, &b) in boundaries.iter().enumerate() {
assert!(
b > total_ns / 10,
"boundary {i} ({b}) must be strictly above 10% pre-buffer ({})",
total_ns / 10,
);
assert!(
b < total_ns - total_ns / 10,
"boundary {i} ({b}) must be strictly below 90% post-buffer ({})",
total_ns - total_ns / 10,
);
}
}
}
#[test]
fn compute_periodic_boundaries_ns_odd_ns_rounding() {
let boundaries = compute_periodic_boundaries_ns(0, 1_000_000_001, 3);
assert_eq!(
boundaries,
vec![300_000_000, 500_000_000, 700_000_000],
"1_000_000_001 ns, N=3 must round each boundary down to \
the multiple-of-100_000_000 nearest the truncating-divide \
result"
);
let boundaries2 = compute_periodic_boundaries_ns(0, 1_000_000_007, 2);
assert_eq!(boundaries2, vec![366_666_669, 633_333_338]);
let boundaries3 = compute_periodic_boundaries_ns(0, 1_000_000_007, 4);
assert_eq!(
boundaries3,
vec![260_000_001, 420_000_002, 580_000_004, 740_000_005]
);
let boundaries4 = compute_periodic_boundaries_ns(12_345, 1_000_012_346, 3);
assert_eq!(
boundaries4,
vec![300_012_345, 500_012_345, 700_012_345],
"window_start offset must be added to the truncated boundary, \
not folded into the truncating-divide"
);
}
#[test]
fn compute_periodic_boundaries_ns_degenerate_window_is_empty() {
assert!(
compute_periodic_boundaries_ns(5_000_000_000, 5_000_000_000, 3).is_empty(),
"a zero-span window must yield no boundaries",
);
assert!(
compute_periodic_boundaries_ns(8_000_000_000, 5_000_000_000, 3).is_empty(),
"an inverted window must yield no boundaries",
);
}
#[test]
fn compute_periodic_boundaries_ns_cold_boot_stays_within_window() {
let window_start_ns: u64 = 3_000_000_000;
let window_end_ns: u64 = 10_000_000_000;
for &n in &[1u32, 3, 6, 12] {
let boundaries = compute_periodic_boundaries_ns(window_start_ns, window_end_ns, n);
assert_eq!(boundaries.len(), n as usize, "N={n} boundary count");
for (i, &b) in boundaries.iter().enumerate() {
assert!(
b > window_start_ns,
"boundary {i} ({b}) must be after window_start ({window_start_ns})",
);
assert!(
b < window_end_ns,
"boundary {i} ({b}) must be before the clamped window_end \
({window_end_ns}) — never spilling into post-workload idle",
);
}
}
}
#[test]
fn compute_periodic_boundaries_ns_tiny_window_below_min_step_is_empty() {
assert!(
compute_periodic_boundaries_ns(0, 70, 64).is_empty(),
"inner window 56 < 65 must yield no boundaries",
);
assert!(compute_periodic_boundaries_ns(0, 50, 64).is_empty());
assert!(
compute_periodic_boundaries_ns(0, 78, 64).is_empty(),
"window == N (64) is conservatively rejected by the >= N+1 guard",
);
let edge = compute_periodic_boundaries_ns(0, 79, 64);
assert_eq!(
edge.len(),
64,
"window == N+1 (65) is the smallest non-empty"
);
assert_eq!(edge[0], 8, "first boundary = pre(7) + 1");
assert_eq!(edge[63], 71, "last boundary = pre(7) + 64");
for w in edge.windows(2) {
assert!(
w[0] < w[1],
"window == N+1 must stay strict at zero slack: {edge:?}",
);
}
assert_eq!(
compute_periodic_boundaries_ns(0, 4, 3),
vec![1, 2, 3],
"window == N+1 at N=3 lands [1,2,3] with unit steps",
);
assert!(compute_periodic_boundaries_ns(0, 3, 3).is_empty());
}
#[test]
fn compute_periodic_boundaries_ns_near_u64_max_no_overflow() {
let wide = compute_periodic_boundaries_ns(0, u64::MAX, 64);
assert_eq!(
wide.len(),
64,
"max-span window must produce all 64 boundaries"
);
for (i, &b) in wide.iter().enumerate() {
assert!(
b < u64::MAX,
"boundary {i} ({b}) must stay below window_end (u64::MAX) — \
the final saturating cast must not clamp",
);
}
for w in wide.windows(2) {
assert!(
w[0] < w[1],
"max-span boundaries must be strictly monotonic; got {w:?}",
);
}
let start = u64::MAX - 79;
let high = compute_periodic_boundaries_ns(start, u64::MAX, 64);
assert_eq!(high.len(), 64, "window==N+1 at high window_start yields 64");
assert_eq!(
high[0],
start + 8,
"first boundary = window_start + pre(7) + 1"
);
assert_eq!(
high[63],
start + 71,
"last boundary = window_start + pre(7) + 64"
);
for (i, &b) in high.iter().enumerate() {
assert!(
b < u64::MAX && b > start,
"boundary {i} ({b}) must stay in (window_start, window_end)",
);
}
}
#[test]
fn resolve_periodic_window_warm_boot_starts_at_anchor() {
let w = resolve_periodic_window(5_000, 3_000, 10_000)
.expect("non-zero anchor must build a window");
assert_eq!(
w.window_start_ns, 5_000,
"warm boot: window_start == anchor"
);
assert_eq!(w.window_end_ns, 15_000, "window_end == anchor + duration");
assert_eq!(w.anchor_ns, 5_000, "anchor_ns == scenario_anchor");
}
#[test]
fn resolve_periodic_window_cold_boot_floats_start_to_prereqs() {
let w = resolve_periodic_window(5_000, 8_000, 10_000)
.expect("non-zero anchor must build a window");
assert_eq!(
w.window_start_ns, 8_000,
"cold boot: window_start floats to prereqs_ready",
);
assert_eq!(
w.window_end_ns, 15_000,
"window_end stays clamped to anchor + duration, not prereqs + duration",
);
assert_eq!(
w.anchor_ns, 5_000,
"anchor_ns stays scenario_anchor, not the floated window_start",
);
}
#[test]
fn resolve_periodic_window_zero_anchor_defers() {
assert!(
resolve_periodic_window(0, 8_000, 10_000).is_none(),
"0 anchor must DEFER, not build an inverted window",
);
assert!(resolve_periodic_window(0, 0, 0).is_none());
}
#[test]
fn resolve_periodic_window_clamps_end_overflow() {
let w = resolve_periodic_window(u64::MAX - 5, 0, 100)
.expect("non-zero anchor must build a window");
assert_eq!(
w.window_end_ns,
u64::MAX,
"anchor + duration must saturate at u64::MAX, not wrap",
);
assert_eq!(
w.window_start_ns,
u64::MAX - 5,
"window_start == anchor (prereqs 0 < anchor)",
);
assert_eq!(w.anchor_ns, u64::MAX - 5);
}
#[test]
fn resolve_periodic_window_cold_boot_too_late_yields_no_boundaries() {
let w = resolve_periodic_window(5_000, 20_000, 10_000)
.expect("non-zero anchor still builds a (possibly inverted) window");
assert_eq!(
w.window_start_ns, 20_000,
"window_start floats to the late prereqs_ready",
);
assert_eq!(
w.window_end_ns, 15_000,
"window_end clamps to anchor + duration",
);
assert!(
w.window_start_ns > w.window_end_ns,
"prereqs past the workload end invert the window",
);
assert_eq!(w.anchor_ns, 5_000);
assert!(
compute_periodic_boundaries_ns(w.window_start_ns, w.window_end_ns, 3).is_empty(),
"an inverted window must yield no boundaries (slicer 0-fires)",
);
}
}