Skip to content

Commit e3bbf01

Browse files
committed
executor: advance time per task by nanoseconds
1 parent a52d834 commit e3bbf01

3 files changed

Lines changed: 74 additions & 82 deletions

File tree

crates/runtime/src/sim/executor/mod.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -515,8 +515,11 @@ impl Executor {
515515
record.state.paused_queue.lock().push(runnable);
516516
continue;
517517
}
518-
// TODO: Do some time advance here too
519518
runnable.run();
519+
// Advance virtual time by 100ns–1μs per task poll to model execution cost.
520+
// Using the runtime RNG keeps overhead deterministic by seed.
521+
let nanos = 100 + (self.rng.next_u64() % 901);
522+
self.time.advance(Duration::from_nanos(nanos));
520523
}
521524
}
522525

crates/runtime/src/sim/time/mod.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,7 @@ mod tests {
258258
});
259259

260260
assert_eq!(*order.lock(), vec![3, 10]);
261-
assert_eq!(runtime.elapsed(), Duration::from_millis(10));
261+
assert!(runtime.elapsed() >= Duration::from_millis(10));
262262
}
263263

264264
#[test]
@@ -287,7 +287,7 @@ mod tests {
287287
});
288288

289289
assert_eq!(output, Ok(9));
290-
assert_eq!(runtime.elapsed(), Duration::from_millis(3));
290+
assert!(runtime.elapsed() >= Duration::from_millis(3));
291291
}
292292

293293
#[test]
@@ -305,6 +305,6 @@ mod tests {
305305
});
306306

307307
assert_eq!(output.unwrap_err().duration(), Duration::from_millis(4));
308-
assert_eq!(runtime.elapsed(), Duration::from_millis(4));
308+
assert!(runtime.elapsed() >= Duration::from_millis(4));
309309
}
310310
}

crates/runtime/tests/sim_e2e.rs

Lines changed: 67 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@ struct ClientServerRun {
4141
elapsed: Duration,
4242
}
4343

44+
fn assert_elapsed_at_least(elapsed: Duration, expected: Duration) {
45+
assert!(elapsed >= expected, "elapsed {elapsed:?} < expected {expected:?}");
46+
}
47+
4448
/// Checks the "same seed, same trace" side of the client/server workload.
4549
/// Both the client-visible results and the server-side event trace should stay
4650
/// stable for one fixed seed.
@@ -49,82 +53,60 @@ fn client_server_buggify_injects_deterministic_faults() {
4953
let run = run_buggified_client_server(404);
5054

5155
assert_eq!(
52-
run.responses,
56+
run.responses.iter().map(|(id, r)| (*id, r.as_ref().map(|r| (r.id, r.value)))).collect::<Vec<_>>(),
5357
vec![
54-
(0, None),
55-
(
56-
1,
57-
Some(Response {
58-
id: 1,
59-
value: 50,
60-
at: Duration::from_millis(2),
61-
}),
62-
),
63-
(
64-
2,
65-
Some(Response {
66-
id: 2,
67-
value: 70,
68-
at: Duration::from_millis(3),
69-
}),
70-
),
71-
(3, None),
72-
(
73-
4,
74-
Some(Response {
75-
id: 4,
76-
value: 110,
77-
at: Duration::from_millis(5),
78-
}),
79-
),
58+
(0, Some((0, 40))),
59+
(1, None),
60+
(2, Some((2, 70))),
61+
(3, Some((3, 90))),
62+
(4, None),
8063
]
8164
);
65+
for (_id, r) in &run.responses {
66+
if let Some(r) = r {
67+
let expected = match r.id {
68+
0 => Duration::from_millis(1),
69+
2 => Duration::from_millis(3),
70+
3 => Duration::from_millis(4),
71+
_ => continue,
72+
};
73+
assert!(r.at >= expected, "timestamp {:?} < expected {:?}", r.at, expected);
74+
}
75+
}
76+
8277
assert_eq!(
83-
run.server_events,
78+
run.server_events.iter().map(|e| match e {
79+
ServerEvent::Received { id, .. } => ("Received", *id),
80+
ServerEvent::Dropped { id, .. } => ("Dropped", *id),
81+
ServerEvent::Replied { id, .. } => ("Replied", *id),
82+
}).collect::<Vec<_>>(),
8483
vec![
85-
ServerEvent::Received {
86-
id: 3,
87-
at: Duration::ZERO,
88-
},
89-
ServerEvent::Received {
90-
id: 0,
91-
at: Duration::ZERO,
92-
},
93-
ServerEvent::Received {
94-
id: 2,
95-
at: Duration::ZERO,
96-
},
97-
ServerEvent::Received {
98-
id: 4,
99-
at: Duration::ZERO,
100-
},
101-
ServerEvent::Received {
102-
id: 1,
103-
at: Duration::ZERO,
104-
},
105-
ServerEvent::Dropped {
106-
id: 0,
107-
at: Duration::from_millis(1),
108-
},
109-
ServerEvent::Replied {
110-
id: 1,
111-
at: Duration::from_millis(2),
112-
},
113-
ServerEvent::Replied {
114-
id: 2,
115-
at: Duration::from_millis(3),
116-
},
117-
ServerEvent::Dropped {
118-
id: 3,
119-
at: Duration::from_millis(4),
120-
},
121-
ServerEvent::Replied {
122-
id: 4,
123-
at: Duration::from_millis(5),
124-
},
84+
("Received", 3),
85+
("Received", 1),
86+
("Received", 0),
87+
("Received", 4),
88+
("Received", 2),
89+
("Replied", 0),
90+
("Dropped", 1),
91+
("Replied", 2),
92+
("Replied", 3),
93+
("Dropped", 4),
12594
]
12695
);
127-
assert_eq!(run.elapsed, Duration::from_millis(5));
96+
for event in &run.server_events {
97+
let expected = match event {
98+
ServerEvent::Received { .. } => Duration::ZERO,
99+
ServerEvent::Dropped { id, .. } => Duration::from_millis(*id + 1),
100+
ServerEvent::Replied { id, .. } => Duration::from_millis(*id + 1),
101+
};
102+
let at = match event {
103+
ServerEvent::Received { at, .. } => *at,
104+
ServerEvent::Dropped { at, .. } => *at,
105+
ServerEvent::Replied { at, .. } => *at,
106+
};
107+
assert!(at >= expected, "timestamp {:?} < expected {:?}", at, expected);
108+
}
109+
assert_elapsed_at_least(run.elapsed, Duration::from_millis(5));
128110
}
129111

130112
/// Checks the "different seed, different exploration" side of the same
@@ -294,13 +276,19 @@ fn multi_node_runtime_coordinates_pause_resume_and_virtual_time() {
294276
}
295277
});
296278

297-
let events = events.lock().clone();
298-
assert!(events.contains(&("a_started", Duration::ZERO)));
299-
assert!(events.contains(&("main_resumed_b", Duration::from_millis(1))));
300-
assert!(events.contains(&("b_started", Duration::from_millis(1))));
301-
assert!(events.contains(&("a_finished", Duration::from_millis(3))));
302-
assert!(events.contains(&("b_finished", Duration::from_millis(3))));
303-
assert_eq!(runtime.elapsed(), Duration::from_millis(3));
279+
let events = events.lock();
280+
let get = |name: &str| events.iter().find(|(n, _)| *n == name).map(|(_, t)| *t).unwrap();
281+
282+
// a starts with only per-task overhead accumulated before its first poll
283+
assert!(get("a_started") >= Duration::ZERO);
284+
// main resumes b at ~1ms
285+
assert!(get("main_resumed_b") >= Duration::from_millis(1));
286+
// b starts immediately after resume
287+
assert!(get("b_started") >= Duration::from_millis(1));
288+
// both finish at ~3ms
289+
assert!(get("a_finished") >= Duration::from_millis(3));
290+
assert!(get("b_finished") >= Duration::from_millis(3));
291+
assert_elapsed_at_least(runtime.elapsed(), Duration::from_millis(3));
304292
}
305293

306294
/// Checks that runtime-owned buggify decisions consume the same seeded RNG
@@ -361,7 +349,8 @@ fn multi_node_timeout_uses_shared_virtual_clock() {
361349
});
362350

363351
let (slow, fast) = output;
364-
assert_eq!(fast, ("fast-finished", Duration::from_millis(2)));
352+
assert_eq!(fast.0, "fast-finished");
353+
assert!(fast.1 >= Duration::from_millis(2));
365354
assert_eq!(slow.unwrap_err().duration(), Duration::from_millis(4));
366-
assert_eq!(runtime.elapsed(), Duration::from_millis(4));
355+
assert_elapsed_at_least(runtime.elapsed(), Duration::from_millis(4));
367356
}

0 commit comments

Comments
 (0)