use simulacra::{Duration, NodeContext, NodeId, TaskSimBuilder, TopologyBuilder};
#[derive(Debug, Clone)]
enum Msg {
Request { seq: u32 },
Reply { seq: u32 },
}
const CLIENT: NodeId = NodeId(0);
const SERVER: NodeId = NodeId(1);
const MAX_ATTEMPTS: u32 = 5;
const BASE_TIMEOUT: Duration = Duration::from_millis(25);
async fn client(ctx: NodeContext<Msg>) {
if ctx.id() != CLIENT {
return; }
for attempt in 1..=MAX_ATTEMPTS {
let seq = attempt;
println!(
"[{:>10}] client: attempt {}/{} (seq={})",
ctx.now(),
attempt,
MAX_ATTEMPTS,
seq
);
ctx.send(SERVER, Msg::Request { seq }).await;
let timeout = BASE_TIMEOUT.saturating_mul(1u64 << (attempt - 1));
match ctx.recv_timeout(timeout).await {
Some(env) => {
if let Msg::Reply { seq: got } = env.payload {
println!(
"[{:>10}] client: reply received (seq={}) after {} attempts",
ctx.now(),
got,
attempt
);
return;
}
}
None => {
println!(
"[{:>10}] client: attempt {} timed out after {}",
ctx.now(),
attempt,
timeout
);
}
}
}
println!(
"[{:>10}] client: giving up after {} attempts",
ctx.now(),
MAX_ATTEMPTS
);
}
async fn server(ctx: NodeContext<Msg>) {
if ctx.id() != SERVER {
return;
}
let mut seen = 0u32;
loop {
let env = ctx.recv().await;
if let Msg::Request { seq } = env.payload {
seen += 1;
if seen < 3 {
println!(
"[{:>10}] server: dropping request seq={} (#{} of 3)",
ctx.now(),
seq,
seen
);
continue;
}
println!("[{:>10}] server: replying to seq={}", ctx.now(), seq);
ctx.send(env.src, Msg::Reply { seq }).await;
return;
}
}
}
fn main() {
let topology = TopologyBuilder::new(2)
.link(0u32, 1u32, Duration::from_millis(5))
.build();
let sim = TaskSimBuilder::<Msg>::new(topology, 0xF00D).build(|ctx| async move {
if ctx.id() == CLIENT {
client(ctx).await;
} else if ctx.id() == SERVER {
server(ctx).await;
}
});
let stats = sim.run();
println!("\n--- Simulation Complete ---");
println!("Final time: {}", stats.final_time);
println!("Messages delivered: {}", stats.messages_delivered);
println!("Tasks completed: {}", stats.tasks_completed);
}