simrs/
execute.rs

1use crate::Simulation;
2use std::time::Duration;
3
4/// Simulation execution trait.
5pub trait Execute {
6    /// Executes the simulation until some stopping condition is reached.
7    /// The condition is implementation-specific.
8    fn execute(self, sim: &mut Simulation);
9}
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12enum EndCondition {
13    Time(Duration),
14    EmptyQueue,
15    Steps(usize),
16}
17
18/// Executor is used for simple execution of an entire simulation.
19///
20/// See the crate level documentation for examples.
21#[derive(Debug, Clone, Copy, PartialEq, Eq)]
22pub struct Executor {
23    end_condition: EndCondition,
24}
25
26impl Executor {
27    /// Simulation will end only once there is no available events in the queue.
28    #[must_use]
29    pub fn unbound() -> Self {
30        Self {
31            end_condition: EndCondition::EmptyQueue,
32        }
33    }
34
35    /// Simulation will be run no longer than the given time.
36    /// It may terminate early if no events are available.
37    #[must_use]
38    pub fn timed(time: Duration) -> Self {
39        Self {
40            end_condition: EndCondition::Time(time),
41        }
42    }
43
44    /// Simulation will execute exactly this many steps, unless we run out of events.
45    #[must_use]
46    pub fn steps(steps: usize) -> Self {
47        Self {
48            end_condition: EndCondition::Steps(steps),
49        }
50    }
51
52    /// Registers a side effect that is called _after_ each simulation step.
53    #[must_use]
54    pub fn side_effect<F>(self, func: F) -> ExecutorWithSideEffect<F>
55    where
56        F: Fn(&Simulation),
57    {
58        ExecutorWithSideEffect {
59            end_condition: self.end_condition,
60            side_effect: func,
61        }
62    }
63}
64
65impl Execute for Executor {
66    fn execute(self, sim: &mut Simulation) {
67        run_with(sim, self.end_condition, |_| {});
68    }
69}
70
71pub struct ExecutorWithSideEffect<F>
72where
73    F: Fn(&Simulation),
74{
75    end_condition: EndCondition,
76    side_effect: F,
77}
78
79impl<F> Execute for ExecutorWithSideEffect<F>
80where
81    F: Fn(&Simulation),
82{
83    fn execute(self, sim: &mut Simulation) {
84        run_with(sim, self.end_condition, self.side_effect);
85    }
86}
87
88fn run_with<F>(sim: &mut Simulation, end_condition: EndCondition, side_effect: F)
89where
90    F: Fn(&Simulation),
91{
92    let step_fn = |sim: &mut Simulation| {
93        let result = sim.step();
94        if result {
95            side_effect(sim);
96        }
97        result
98    };
99    match end_condition {
100        EndCondition::Time(time) => execute_until(sim, time, step_fn),
101        EndCondition::EmptyQueue => execute_until_empty(sim, step_fn),
102        EndCondition::Steps(steps) => execute_steps(sim, steps, step_fn),
103    }
104}
105
106fn execute_until_empty<F>(sim: &mut Simulation, step: F)
107where
108    F: Fn(&mut Simulation) -> bool,
109{
110    while step(sim) {}
111}
112
113fn execute_until<F>(sim: &mut Simulation, time: Duration, step: F)
114where
115    F: Fn(&mut Simulation) -> bool,
116{
117    while sim.scheduler.peek().map_or(false, |e| e.time() <= time) {
118        step(sim);
119    }
120}
121
122fn execute_steps<F>(sim: &mut Simulation, steps: usize, step: F)
123where
124    F: Fn(&mut Simulation) -> bool,
125{
126    for _ in 0..steps {
127        if !step(sim) {
128            break;
129        }
130    }
131}
132
133#[cfg(test)]
134mod test {
135    use super::*;
136    use crate::Component;
137
138    struct TestComponent {
139        counter: crate::Key<usize>,
140    }
141
142    #[derive(Debug)]
143    struct TestEvent;
144
145    impl Component for TestComponent {
146        type Event = TestEvent;
147
148        fn process_event(
149            &self,
150            self_id: crate::ComponentId<Self::Event>,
151            _event: &Self::Event,
152            scheduler: &mut crate::Scheduler,
153            state: &mut crate::State,
154        ) {
155            let counter = state.get_mut(self.counter).unwrap();
156            *counter += 1;
157            if *counter < 10 {
158                scheduler.schedule(Duration::from_secs(2), self_id, TestEvent);
159            }
160        }
161    }
162
163    #[test]
164    fn test_create_executor() {
165        assert_eq!(
166            Executor::unbound(),
167            Executor {
168                end_condition: EndCondition::EmptyQueue
169            }
170        );
171        assert_eq!(
172            Executor::timed(Duration::default()),
173            Executor {
174                end_condition: EndCondition::Time(Duration::default())
175            }
176        );
177        assert_eq!(
178            Executor::steps(7),
179            Executor {
180                end_condition: EndCondition::Steps(7)
181            }
182        );
183        // Bonus: satisfy codecov on derive
184        assert_eq!(&format!("{:?}", TestEvent), "TestEvent");
185    }
186
187    #[test]
188    fn test_steps() {
189        let mut sim = Simulation::default();
190        let counter_key = sim.state.insert(0_usize);
191        let component = sim.add_component(TestComponent {
192            counter: counter_key,
193        });
194        sim.schedule(Duration::default(), component, TestEvent);
195        Executor::steps(10).execute(&mut sim);
196        assert_eq!(sim.state.get(counter_key), Some(&10));
197    }
198
199    #[test]
200    fn test_steps_stops_before() {
201        let mut sim = Simulation::default();
202        let counter_key = sim.state.insert(0_usize);
203        let component = sim.add_component(TestComponent {
204            counter: counter_key,
205        });
206        sim.schedule(Duration::default(), component, TestEvent);
207        // After 10 steps there are no events, so it will not execute all 100
208        Executor::steps(100).execute(&mut sim);
209        assert_eq!(sim.state.get(counter_key), Some(&10));
210    }
211
212    #[test]
213    fn test_timed() {
214        let mut sim = Simulation::default();
215        let counter_key = sim.state.insert(0_usize);
216        let component = sim.add_component(TestComponent {
217            counter: counter_key,
218        });
219        sim.schedule(Duration::default(), component, TestEvent);
220        Executor::timed(Duration::from_secs(6)).execute(&mut sim);
221        assert_eq!(sim.state.get(counter_key), Some(&4));
222        assert_eq!(sim.scheduler.clock().time(), Duration::from_secs(6));
223    }
224
225    #[test]
226    fn test_timed_clock_stops_early() {
227        let mut sim = Simulation::default();
228        let counter_key = sim.state.insert(0_usize);
229        let component = sim.add_component(TestComponent {
230            counter: counter_key,
231        });
232        sim.schedule(Duration::default(), component, TestEvent);
233        Executor::timed(Duration::from_secs(5)).execute(&mut sim);
234        assert_eq!(sim.state.get(counter_key), Some(&3));
235        assert_eq!(sim.scheduler.clock().time(), Duration::from_secs(4));
236    }
237}