1use crate::clock::{Clock, Micros};
17use crate::disk::{DiskError, VirtualDisk};
18use crate::network::{Message, Network, NodeId, SendOutcome};
19use crate::rng::{Seed, SimRng};
20use crate::scenario::Event;
21use std::collections::BTreeMap;
22
23pub type TaskId = u64;
25
26pub type TaskBody = Box<dyn FnMut(&mut NodeContext<'_>) -> TaskState>;
29
30#[derive(Debug, Clone, Copy, PartialEq, Eq)]
32pub enum TaskState {
33 Yield,
35 SleepFor(Micros),
37 WaitForMessage,
39 Done,
41}
42
43#[derive(Debug, Clone, Copy, PartialEq, Eq)]
44enum Status {
45 Ready,
46 Sleeping(Micros),
47 WaitingMessage,
48 Done,
49}
50
51struct Task {
52 node: NodeId,
53 name: String,
54 status: Status,
55 rng: SimRng,
56 body: TaskBody,
57}
58
59pub struct NodeContext<'a> {
62 node: NodeId,
63 clock: &'a Clock,
64 rng: &'a mut SimRng,
65 net: &'a mut Network,
66 disk: &'a mut VirtualDisk,
67 events: &'a mut Vec<Event>,
68}
69
70impl NodeContext<'_> {
71 pub fn node(&self) -> NodeId {
73 self.node
74 }
75
76 pub fn now(&self) -> Micros {
78 self.clock.now()
79 }
80
81 pub fn node_now(&self) -> i64 {
83 self.clock.node_now(self.node)
84 }
85
86 pub fn rng(&mut self) -> &mut SimRng {
88 &mut *self.rng
89 }
90
91 pub fn send(&mut self, to: NodeId, payload: impl Into<Vec<u8>>) -> SendOutcome {
93 let outcome = self.net.send(
94 self.node,
95 to,
96 payload.into(),
97 self.clock.now(),
98 &mut *self.rng,
99 );
100 match &outcome {
101 SendOutcome::Scheduled {
102 seq,
103 deliver_at,
104 duplicated,
105 reordered,
106 } => {
107 self.events.push(Event::MessageSent {
108 from: self.node,
109 to,
110 seq: *seq,
111 deliver_at: *deliver_at,
112 });
113 if *duplicated {
114 self.events.push(Event::MessageDuplicated {
115 from: self.node,
116 to,
117 seq: *seq,
118 });
119 }
120 if *reordered {
121 self.events.push(Event::MessageReordered {
122 from: self.node,
123 to,
124 seq: *seq,
125 });
126 }
127 }
128 SendOutcome::Dropped { seq, reason } => {
129 self.events.push(Event::MessageDropped {
130 from: self.node,
131 to,
132 seq: *seq,
133 reason: *reason,
134 });
135 }
136 }
137 outcome
138 }
139
140 pub fn try_recv(&mut self) -> Option<Message> {
142 self.net.try_recv(self.node)
143 }
144
145 pub fn disk_append(&mut self, path: &str, bytes: &[u8]) -> Result<usize, DiskError> {
147 match self.disk.append(path, bytes) {
148 Ok(written) => {
149 if written < bytes.len() {
150 self.events.push(Event::DiskTornWrite {
151 node: self.node,
152 path: path.to_string(),
153 requested: bytes.len(),
154 written,
155 });
156 } else {
157 self.events.push(Event::DiskWrite {
158 node: self.node,
159 path: path.to_string(),
160 len: written,
161 });
162 }
163 Ok(written)
164 }
165 Err(error) => {
166 self.events.push(Event::DiskWriteFailed {
167 node: self.node,
168 path: path.to_string(),
169 });
170 Err(error)
171 }
172 }
173 }
174
175 pub fn disk_fsync(&mut self, path: &str) -> Result<(), DiskError> {
177 match self.disk.fsync(path) {
178 Ok(()) => {
179 self.events.push(Event::DiskFsync {
180 node: self.node,
181 path: path.to_string(),
182 durable_len: self.disk.durable_len(path),
183 });
184 Ok(())
185 }
186 Err(error) => {
187 self.events.push(Event::DiskFsyncFailed {
188 node: self.node,
189 path: path.to_string(),
190 });
191 Err(error)
192 }
193 }
194 }
195
196 pub fn disk_read(&self, path: &str) -> Vec<u8> {
198 self.disk.read(path)
199 }
200
201 pub fn disk_read_durable(&self, path: &str) -> Vec<u8> {
203 self.disk.read_durable(path)
204 }
205
206 pub fn log(&mut self, message: impl Into<String>) {
208 self.events.push(Event::Custom {
209 node: self.node,
210 message: message.into(),
211 });
212 }
213}
214
215pub struct Runtime {
217 clock: Clock,
218 rng: SimRng,
219 tasks: BTreeMap<TaskId, Task>,
220 next_task_id: TaskId,
221}
222
223impl Runtime {
224 pub fn new(seed: Seed) -> Self {
226 Self {
227 clock: Clock::new(),
228 rng: SimRng::from_seed(seed),
229 tasks: BTreeMap::new(),
230 next_task_id: 1,
231 }
232 }
233
234 pub fn clock(&self) -> &Clock {
236 &self.clock
237 }
238
239 pub(crate) fn clock_mut(&mut self) -> &mut Clock {
240 &mut self.clock
241 }
242
243 pub fn spawn(&mut self, node: NodeId, name: &str, body: TaskBody) -> TaskId {
246 let id = self.next_task_id;
247 self.next_task_id += 1;
248 let rng = self.rng.fork();
249 self.tasks.insert(
250 id,
251 Task {
252 node,
253 name: name.to_string(),
254 status: Status::Ready,
255 rng,
256 body,
257 },
258 );
259 id
260 }
261
262 pub(crate) fn task_node(&self, task: TaskId) -> NodeId {
263 self.tasks[&task].node
264 }
265
266 pub(crate) fn wake_due(&mut self) {
268 let now = self.clock.now();
269 for task in self.tasks.values_mut() {
270 if let Status::Sleeping(until) = task.status {
271 if until <= now {
272 task.status = Status::Ready;
273 }
274 }
275 }
276 }
277
278 pub(crate) fn wake_receivers(&mut self, node: NodeId) {
280 for task in self.tasks.values_mut() {
281 if task.node == node && task.status == Status::WaitingMessage {
282 task.status = Status::Ready;
283 }
284 }
285 }
286
287 pub(crate) fn pick_ready(&mut self) -> Option<TaskId> {
290 let ready: Vec<TaskId> = self
291 .tasks
292 .iter()
293 .filter(|(_, task)| task.status == Status::Ready)
294 .map(|(&id, _)| id)
295 .collect();
296 if ready.is_empty() {
297 None
298 } else {
299 Some(ready[self.rng.below(ready.len() as u64) as usize])
300 }
301 }
302
303 pub(crate) fn step(
305 &mut self,
306 task_id: TaskId,
307 net: &mut Network,
308 disk: &mut VirtualDisk,
309 events: &mut Vec<Event>,
310 ) {
311 let now = self.clock.now();
312 let task = self.tasks.get_mut(&task_id).expect("task must exist");
313 let node = task.node;
314 let state = {
315 let mut ctx = NodeContext {
316 node,
317 clock: &self.clock,
318 rng: &mut task.rng,
319 net: &mut *net,
320 disk: &mut *disk,
321 events: &mut *events,
322 };
323 (task.body)(&mut ctx)
324 };
325 match state {
326 TaskState::Yield => task.status = Status::Ready,
327 TaskState::SleepFor(delta) => task.status = Status::Sleeping(now + delta),
328 TaskState::WaitForMessage => {
329 task.status = if net.inbox_len(node) > 0 {
330 Status::Ready
331 } else {
332 Status::WaitingMessage
333 };
334 }
335 TaskState::Done => {
336 task.status = Status::Done;
337 events.push(Event::TaskDone {
338 task: task_id,
339 node,
340 name: task.name.clone(),
341 });
342 }
343 }
344 }
345
346 pub(crate) fn remove_tasks_of(&mut self, node: NodeId) -> usize {
349 let before = self.tasks.len();
350 self.tasks.retain(|_, task| task.node != node);
351 before - self.tasks.len()
352 }
353
354 pub(crate) fn next_wake(&self) -> Option<Micros> {
356 self.tasks
357 .values()
358 .filter_map(|task| match task.status {
359 Status::Sleeping(until) => Some(until),
360 _ => None,
361 })
362 .min()
363 }
364
365 pub(crate) fn all_done(&self) -> bool {
367 self.tasks.values().all(|task| task.status == Status::Done)
368 }
369
370 pub(crate) fn unfinished_count(&self) -> usize {
372 self.tasks
373 .values()
374 .filter(|task| task.status != Status::Done)
375 .count()
376 }
377}
378
379#[cfg(test)]
380mod tests {
381 use super::*;
382 use crate::network::LinkConfig;
383
384 const A: NodeId = NodeId(1);
385
386 fn harness(seed: u64) -> (Runtime, Network, VirtualDisk, Vec<Event>) {
387 (
388 Runtime::new(Seed::new(seed)),
389 Network::new(LinkConfig::default()),
390 VirtualDisk::new(),
391 Vec::new(),
392 )
393 }
394
395 #[test]
396 fn tasks_run_as_cooperative_state_machines() {
397 let (mut runtime, mut net, mut disk, mut events) = harness(1);
398 let task = runtime.spawn(A, "counter", {
399 let mut steps = 0;
400 Box::new(move |_ctx| {
401 steps += 1;
402 if steps >= 3 {
403 TaskState::Done
404 } else {
405 TaskState::Yield
406 }
407 })
408 });
409
410 runtime.step(task, &mut net, &mut disk, &mut events);
411 runtime.step(task, &mut net, &mut disk, &mut events);
412 assert!(!runtime.all_done());
413 assert_eq!(runtime.unfinished_count(), 1);
414 runtime.step(task, &mut net, &mut disk, &mut events);
415 assert!(runtime.all_done());
416 assert!(events.iter().any(|event| matches!(
417 event,
418 Event::TaskDone { task: done, name, .. } if *done == task && name == "counter"
419 )));
420 }
421
422 #[test]
423 fn sleeping_tasks_wake_at_their_deadline() {
424 let (mut runtime, mut net, mut disk, mut events) = harness(2);
425 let task = runtime.spawn(A, "sleeper", {
426 let mut slept = false;
427 Box::new(move |_ctx| {
428 if slept {
429 TaskState::Done
430 } else {
431 slept = true;
432 TaskState::SleepFor(100)
433 }
434 })
435 });
436
437 runtime.step(task, &mut net, &mut disk, &mut events);
438 assert_eq!(runtime.next_wake(), Some(100));
439 runtime.wake_due();
440 assert_eq!(runtime.pick_ready(), None);
441
442 runtime.clock_mut().advance_to(100).unwrap();
443 runtime.wake_due();
444 assert_eq!(runtime.pick_ready(), Some(task));
445 }
446
447 #[test]
448 fn seeded_ready_pick_is_reproducible() {
449 let pick_sequence = |seed: u64| {
450 let mut runtime = Runtime::new(Seed::new(seed));
451 for _ in 0..5 {
452 runtime.spawn(A, "idle", Box::new(|_| TaskState::Yield));
453 }
454 (0..20).map(|_| runtime.pick_ready()).collect::<Vec<_>>()
455 };
456 assert_eq!(pick_sequence(9), pick_sequence(9));
457 assert_ne!(pick_sequence(9), pick_sequence(10));
458 }
459
460 #[test]
461 fn crash_drops_tasks_and_receiver_wake_works() {
462 let (mut runtime, mut net, mut disk, mut events) = harness(3);
463 let b = NodeId(2);
464 runtime.spawn(b, "waiter", Box::new(|_| TaskState::WaitForMessage));
465 runtime.spawn(b, "waiter2", Box::new(|_| TaskState::WaitForMessage));
466 let parked = runtime.tasks.len();
467 assert_eq!(parked, 2);
468
469 let task = runtime.pick_ready().unwrap();
471 runtime.step(task, &mut net, &mut disk, &mut events);
472 runtime.wake_receivers(b);
473 assert_eq!(runtime.remove_tasks_of(b), 2);
477 assert_eq!(runtime.unfinished_count(), 0);
478 }
479}