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,
}
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(
scenario_anchor_ns: u64,
workload_duration: Duration,
num_snapshots: u32,
) -> Vec<u64> {
if num_snapshots == 0 {
return Vec::new();
}
let n = num_snapshots as u128;
let total_ns = workload_duration.as_nanos();
let pre_buffer = total_ns / 10;
let window = total_ns.saturating_sub(2u128.saturating_mul(pre_buffer));
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 = (scenario_anchor_ns as u128).saturating_add(offset);
boundaries.push(u64::try_from(absolute).unwrap_or(u64::MAX));
}
boundaries
}
#[cfg(test)]
mod tests {
use super::*;
#[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 d = std::time::Duration::from_secs(10);
let boundaries = compute_periodic_boundaries_ns(0, d, 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 start + 0.5·d (midpoint of 10 s = 5 s)",
);
}
#[test]
fn compute_periodic_boundaries_ns_n3_quartile_landings() {
let d = std::time::Duration::from_secs(10);
let boundaries = compute_periodic_boundaries_ns(0, d, 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_anchor_offset() {
let d = std::time::Duration::from_secs(10);
let anchor_ns: u64 = 2_000_000_000;
let boundaries = compute_periodic_boundaries_ns(anchor_ns, d, 1);
assert_eq!(boundaries.len(), 1);
assert_eq!(
boundaries[0], 7_000_000_000,
"anchor(2 s) + midpoint(5 s) = 7 s",
);
}
#[test]
fn compute_periodic_boundaries_ns_n0_yields_empty() {
let d = std::time::Duration::from_secs(10);
let boundaries = compute_periodic_boundaries_ns(0, d, 0);
assert!(
boundaries.is_empty(),
"N=0 must yield no boundaries, got {boundaries:?}",
);
}
#[test]
fn compute_periodic_boundaries_ns_strictly_monotonic() {
let d = std::time::Duration::from_secs(60);
for &n in &[1u32, 2, 3, 7, 16, 32, 64] {
let boundaries = compute_periodic_boundaries_ns(0, d, 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 d = std::time::Duration::from_secs(10);
let total_ns = d.as_nanos() as u64;
for &n in &[1u32, 4, 8] {
let boundaries = compute_periodic_boundaries_ns(0, d, 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 d = std::time::Duration::from_nanos(1_000_000_001);
let boundaries = compute_periodic_boundaries_ns(0, d, 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 d2 = std::time::Duration::from_nanos(1_000_000_007);
let boundaries2 = compute_periodic_boundaries_ns(0, d2, 2);
assert_eq!(boundaries2, vec![366_666_669, 633_333_338]);
let boundaries3 = compute_periodic_boundaries_ns(0, d2, 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, d, 3);
assert_eq!(
boundaries4,
vec![300_012_345, 500_012_345, 700_012_345],
"anchor offset must be added to the truncated boundary, \
not folded into the truncating-divide"
);
}
}