//! Measure per-connection vsock-mux overhead. Each iteration:
//!   open unix sock → muxer creates vsock → agent serves CONTROL
//!   "probe" (single-shot, no fork) → ack → close.
//!
//! That's the floor of any host→agent RPC. If a vsock-mux pool
//! eliminates the connection-setup phase, the savings are bounded
//! by what THIS measures — anything beyond that is the agent's
//! own work + frame copy + IRQ delivery. Run on a baked snapshot:
//!
//!   cargo run --release --example _vsock_rtt_probe
//!
//! Reports min/p50/p95/max over 200 iterations.

use std::time::{Duration, Instant};
use supermachine::Image;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let img = Image::builder("rust:1-slim")
        .with_name("rust_1_slim_rtt_probe")
        .build()?;
    let pool = img.pool().min(1).max(1).restore_on_release(false).build()?;
    // Acquire once so the pool's worker is fully restored + agent
    // is up. Subsequent RPCs go to the live worker.
    let vm = pool.acquire()?;

    // Warm up the path (first call after restore touches some slow
    // paths on macOS, doesn't reflect steady-state).
    for _ in 0..10 {
        let _ = vm.exec_builder()
            .argv(["true"])
            .timeout(Duration::from_millis(500))
            .output()?;
    }

    let n = 200usize;
    let mut samples = Vec::with_capacity(n);
    for _ in 0..n {
        let t0 = Instant::now();
        // `true` is a builtin in dash/bash, exits 0 immediately. Most
        // of the time goes to: open unix sock, muxer OP_REQUEST,
        // guest accept, agent dispatch, agent waitpid on /bin/true,
        // ack frame, close. Fork+exec of /bin/true is O(100µs) on
        // Linux ARM64; the rest is pure plumbing.
        let _ = vm.exec_builder()
            .argv(["true"])
            .timeout(Duration::from_millis(500))
            .output()?;
        samples.push(t0.elapsed());
    }
    samples.sort();
    let min = samples[0];
    let p50 = samples[n / 2];
    let p95 = samples[(n * 95) / 100];
    let max = samples[n - 1];
    let mean: Duration = samples.iter().sum::<Duration>() / n as u32;
    println!(
        "[rtt] n={n}  min={:>5} µs  p50={:>5} µs  p95={:>5} µs  max={:>5} µs  mean={:>5} µs",
        min.as_micros(),
        p50.as_micros(),
        p95.as_micros(),
        max.as_micros(),
        mean.as_micros(),
    );
    Ok(())
}
