gwr_engine/
engine.rs

1// Copyright (c) 2023 Graphcore Ltd. All rights reserved.
2
3use std::cell::RefCell;
4use std::future::Future;
5use std::rc::Rc;
6
7use gwr_track::entity::{Entity, toplevel};
8use gwr_track::tracker::stdout_tracker;
9use gwr_track::{Tracker, trace};
10
11use crate::executor::{self, Executor, Spawner};
12use crate::time::clock::Clock;
13use crate::types::{Component, Eventable, SimResult};
14
15/// Use a default clock frequency of 1GHz.
16const DEFAULT_CLOCK_MHZ: f64 = 1000.0;
17
18pub struct Registry {
19    entity: Rc<Entity>,
20    components: RefCell<Vec<Component>>,
21}
22
23impl Registry {
24    fn new(parent: &Rc<Entity>) -> Self {
25        Self {
26            entity: Rc::new(Entity::new(parent, "registry")),
27            components: RefCell::new(Vec::new()),
28        }
29    }
30
31    pub fn spawn_components(&self, spawner: &Spawner) {
32        let mut guard = self.components.borrow_mut();
33
34        trace!(self.entity ; "Spawning {} components", guard.len());
35
36        for component in guard.drain(..) {
37            spawner.spawn(async move { component.run().await });
38        }
39    }
40
41    pub fn register(&self, component: Component) {
42        let mut guard = self.components.borrow_mut();
43        guard.push(component);
44    }
45}
46
47pub struct Engine {
48    pub executor: Executor,
49    spawner: Spawner,
50    toplevel: Rc<Entity>,
51    tracker: Tracker,
52    registry: Registry,
53}
54
55impl Engine {
56    /// Create a standalone engine.
57    pub fn new(tracker: &Tracker) -> Self {
58        let toplevel = toplevel(tracker, "top");
59        let (executor, spawner) = executor::new_executor_and_spawner(&toplevel);
60        let registry = Registry::new(&toplevel);
61        Self {
62            executor,
63            spawner,
64            toplevel,
65            tracker: tracker.clone(),
66            registry,
67        }
68    }
69
70    /// Register a component that will be run as the simulation starts
71    pub fn register(&self, component: Component) {
72        self.registry.register(component);
73    }
74
75    pub fn run(&mut self) -> SimResult {
76        self.registry.spawn_components(&self.spawner);
77
78        // Pass an atomic bool that will never be set to true
79        let finished = Rc::new(RefCell::new(false));
80        self.executor.run(&finished)
81    }
82
83    pub fn run_until<T: Default + Copy + 'static>(&mut self, event: Eventable<T>) -> SimResult {
84        self.registry.spawn_components(&self.spawner);
85
86        // Create an atomic bool that is set to true as soon as the event fires.
87        let finished = Rc::new(RefCell::new(false));
88        {
89            let finished = finished.clone();
90            self.spawner.spawn(async move {
91                event.listen().await;
92                *finished.borrow_mut() = true;
93                Ok(())
94            });
95        }
96
97        self.executor.run(&finished)
98    }
99
100    #[must_use]
101    pub fn spawner(&self) -> Spawner {
102        self.spawner.clone()
103    }
104
105    pub fn spawn(&self, future: impl Future<Output = SimResult> + 'static) {
106        self.spawner.spawn(future);
107    }
108
109    pub fn set_randomize_task_order(&self, randomize: bool) {
110        self.executor.set_randomize_task_order(randomize);
111    }
112
113    pub fn set_task_order_seed(&self, seed: u64) {
114        self.executor.set_task_order_seed(seed);
115    }
116
117    #[must_use]
118    pub fn default_clock(&mut self) -> Clock {
119        self.executor.get_clock(DEFAULT_CLOCK_MHZ)
120    }
121
122    #[must_use]
123    pub fn clock_hz(&mut self, freq_hz: f64) -> Clock {
124        self.executor.get_clock(freq_hz / 1_000_000.0)
125    }
126
127    #[must_use]
128    pub fn clock_khz(&mut self, freq_khz: f64) -> Clock {
129        self.executor.get_clock(freq_khz / 1000.0)
130    }
131
132    #[must_use]
133    pub fn clock_mhz(&mut self, freq_mhz: f64) -> Clock {
134        self.executor.get_clock(freq_mhz)
135    }
136
137    #[must_use]
138    pub fn clock_ghz(&mut self, freq_ghz: f64) -> Clock {
139        self.executor.get_clock(freq_ghz * 1000.0)
140    }
141
142    #[must_use]
143    pub fn time_now_ns(&self) -> f64 {
144        self.executor.time_now_ns()
145    }
146
147    #[must_use]
148    pub fn top(&self) -> &Rc<Entity> {
149        &self.toplevel
150    }
151
152    #[must_use]
153    pub fn tracker(&self) -> Tracker {
154        self.tracker.clone()
155    }
156}
157
158/// Create a default engine that sends [`Track`](gwr_track::Track) events to
159/// stdout.
160///
161/// This is provided to keep documentation examples simple with fewer
162/// concepts to have to consider at once.
163impl Default for Engine {
164    fn default() -> Self {
165        let tracker = stdout_tracker(log::Level::Info);
166        Self::new(&tracker)
167    }
168}
169
170impl Drop for Engine {
171    fn drop(&mut self) {
172        // The tracker can be using a buffered writer and so it needs to be shut down
173        // cleanly to ensure that it is flushed properly.
174        self.tracker.shutdown();
175    }
176}